1690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell/*
2690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Copyright (C) 2012 The Android Open Source Project
3690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *
4690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Licensed under the Apache License, Version 2.0 (the "License");
5690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * you may not use this file except in compliance with the License.
6690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * You may obtain a copy of the License at
7690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *
8690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *      http://www.apache.org/licenses/LICENSE-2.0
9690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *
10690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Unless required by applicable law or agreed to in writing, software
11690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * distributed under the License is distributed on an "AS IS" BASIS,
12690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * See the License for the specific language governing permissions and
14690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * limitations under the License.
15690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */
16690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
17690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellpackage android.app;
18690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
19690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport com.android.internal.R;
200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brownimport com.android.internal.app.MediaRouteDialogPresenter;
21690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
22f6d87ec193f17e8dad82c9994ba7a58e975d364bAlan Viveretteimport android.annotation.NonNull;
23690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.content.Context;
2470e11e50eecfc8f1dfb76316d099e4331ebd28f7Adam Powellimport android.content.ContextWrapper;
25690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.content.res.TypedArray;
26690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.graphics.Canvas;
27d9125cf9df3fd97082bb801f23758eec8f8f33e3yangrenimport android.graphics.drawable.AnimationDrawable;
28690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.graphics.drawable.Drawable;
29690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.media.MediaRouter;
30f7e0a328c939fa61792d4b685e8f9047a3a0ad90Adam Powellimport android.media.MediaRouter.RouteGroup;
31690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.media.MediaRouter.RouteInfo;
32690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.util.AttributeSet;
33690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.view.SoundEffectConstants;
34690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.view.View;
35690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
36690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellpublic class MediaRouteButton extends View {
370abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private final MediaRouter mRouter;
380abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private final MediaRouterCallback mCallback;
39690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
40690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private int mRouteTypes;
41690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
42101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    private boolean mAttachedToWindow;
43101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich
44690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private Drawable mRemoteIndicator;
45690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private boolean mRemoteActive;
462ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    private boolean mIsConnecting;
47690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
48690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private int mMinWidth;
49690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private int mMinHeight;
50690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
51b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell    private OnClickListener mExtendedSettingsClickListener;
52b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell
530abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    // The checked state is used when connected to a remote route.
542ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    private static final int[] CHECKED_STATE_SET = {
552ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        R.attr.state_checked
562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell    };
572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    // The activated state is used while connecting to a remote route.
59690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private static final int[] ACTIVATED_STATE_SET = {
60690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        R.attr.state_activated
61690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    };
62690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
63690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public MediaRouteButton(Context context) {
64690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        this(context, null);
65690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
66690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
67690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public MediaRouteButton(Context context, AttributeSet attrs) {
6874d762a3ee7dd66fe9ce44f3232159811af8d5d8Adam Powell        this(context, attrs, com.android.internal.R.attr.mediaRouteButtonStyle);
69690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
70690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
71690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
72617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        this(context, attrs, defStyleAttr, 0);
73617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    }
74617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette
75617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette    public MediaRouteButton(
76617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette            Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
77617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        super(context, attrs, defStyleAttr, defStyleRes);
78690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
79b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mCallback = new MediaRouterCallback();
81690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
82617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette        final TypedArray a = context.obtainStyledAttributes(attrs,
83617feb99a06e7ffb3894e86a286bf30e085f321aAlan Viverette                com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, defStyleRes);
84690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        setRemoteIndicatorDrawable(a.getDrawable(
85690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                com.android.internal.R.styleable.MediaRouteButton_externalRouteEnabledDrawable));
86690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        mMinWidth = a.getDimensionPixelSize(
87690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                com.android.internal.R.styleable.MediaRouteButton_minWidth, 0);
88690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        mMinHeight = a.getDimensionPixelSize(
89690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                com.android.internal.R.styleable.MediaRouteButton_minHeight, 0);
90849df0b5a7a5ef72610d687a8b1c2f0a23f873abAdam Powell        final int routeTypes = a.getInteger(
91849df0b5a7a5ef72610d687a8b1c2f0a23f873abAdam Powell                com.android.internal.R.styleable.MediaRouteButton_mediaRouteTypes,
92849df0b5a7a5ef72610d687a8b1c2f0a23f873abAdam Powell                MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
93690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        a.recycle();
94690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
95690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        setClickable(true);
96849df0b5a7a5ef72610d687a8b1c2f0a23f873abAdam Powell
97849df0b5a7a5ef72610d687a8b1c2f0a23f873abAdam Powell        setRouteTypes(routeTypes);
98690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
99690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
1010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Gets the media route types for filtering the routes that the user can
1020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * select using the media route chooser dialog.
1030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
1040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @return The route types.
1050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
1060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public int getRouteTypes() {
1070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return mRouteTypes;
1080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
1090abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
1110abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Sets the types of routes that will be shown in the media route chooser dialog
1120abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * launched by this button.
1130abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
1140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The route types to match.
1150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
1160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public void setRouteTypes(int types) {
1170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mRouteTypes != types) {
1180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (mAttachedToWindow && mRouteTypes != 0) {
1190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                mRouter.removeCallback(mCallback);
1200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
1210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mRouteTypes = types;
1230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (mAttachedToWindow && types != 0) {
1250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                mRouter.addCallback(types, mCallback,
1260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
1270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
1280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1290abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
130690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1310abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
132690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1330abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public void setExtendedSettingsClickListener(OnClickListener listener) {
1340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mExtendedSettingsClickListener = listener;
135690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
136690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1370abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
1380abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Show the route chooser or controller dialog.
1390abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * <p>
1400abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * If the default route is selected or if the currently selected route does
1410abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * not match the {@link #getRouteTypes route types}, then shows the route chooser dialog.
1420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Otherwise, shows the route controller dialog to offer the user
1430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * a choice to disconnect from the route or perform other control actions
1440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * such as setting the route's volume.
1450abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p><p>
1460abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * This will attach a {@link DialogFragment} to the containing Activity.
1470abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * </p>
1480abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
1490abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public void showDialog() {
1500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        showDialogInternal();
1510abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
1520abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1530abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    boolean showDialogInternal() {
1540abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!mAttachedToWindow) {
1550abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return false;
156690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
157690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
1580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        DialogFragment f = MediaRouteDialogPresenter.showDialogFragment(getActivity(),
1590abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                mRouteTypes, mExtendedSettingsClickListener);
1600abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return f != null;
1610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
1620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private Activity getActivity() {
1640abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // Gross way of unwrapping the Activity so we can get the FragmentManager
1650abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        Context context = getContext();
1660abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        while (context instanceof ContextWrapper) {
1670abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (context instanceof Activity) {
1680abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                return (Activity)context;
169690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
1700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            context = ((ContextWrapper)context).getBaseContext();
171690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        throw new IllegalStateException("The MediaRouteButton's Context is not an Activity.");
173690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
174690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
175b265617b810082ec743389fa0468d1df91d66f68Vladislav Kaznacheev    @Override
176b265617b810082ec743389fa0468d1df91d66f68Vladislav Kaznacheev    public void setContentDescription(CharSequence contentDescription) {
177b265617b810082ec743389fa0468d1df91d66f68Vladislav Kaznacheev        super.setContentDescription(contentDescription);
1786a944ca17547e520ed4988125ee4c1f172c87946Vladislav Kaznacheev        setTooltipText(contentDescription);
179e4681878702edaf0833294b3f1ce835705d3ad04Adam Powell    }
180e4681878702edaf0833294b3f1ce835705d3ad04Adam Powell
181e4681878702edaf0833294b3f1ce835705d3ad04Adam Powell    @Override
1820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public boolean performClick() {
1830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        // Send the appropriate accessibility events and call listeners
1840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        boolean handled = super.performClick();
1850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (!handled) {
1860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            playSoundEffect(SoundEffectConstants.CLICK);
1870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
1880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return showDialogInternal() || handled;
1890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
1900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    @Override
192690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    protected int[] onCreateDrawableState(int extraSpace) {
193690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
1942ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
1952ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        // Technically we should be handling this more completely, but these
1962ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        // are implementation details here. Checked is used to express the connecting
1972ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        // drawable state and it's mutually exclusive with activated for the purposes
1982ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        // of state selection here.
1992ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        if (mIsConnecting) {
2002ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
2012ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        } else if (mRemoteActive) {
202690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            mergeDrawableStates(drawableState, ACTIVATED_STATE_SET);
203690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
204690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        return drawableState;
205690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
206690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
207690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
208690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    protected void drawableStateChanged() {
209690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        super.drawableStateChanged();
210690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
211ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette        final Drawable remoteIndicator = mRemoteIndicator;
212ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette        if (remoteIndicator != null && remoteIndicator.isStateful()
213ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette                && remoteIndicator.setState(getDrawableState())) {
214ad0020f8075ecf768cb610d60dbb167d41f0fbe5Alan Viverette            invalidateDrawable(remoteIndicator);
215690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
216690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
217690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
2180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private void setRemoteIndicatorDrawable(Drawable d) {
2190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mRemoteIndicator != null) {
2200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mRemoteIndicator.setCallback(null);
2210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            unscheduleDrawable(mRemoteIndicator);
2220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
2230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mRemoteIndicator = d;
2240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (d != null) {
2250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            d.setCallback(this);
2260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            d.setState(getDrawableState());
2270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            d.setVisible(getVisibility() == VISIBLE, false);
2280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
2290abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
2300abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        refreshDrawableState();
2310abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
2320abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
233690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
234f6d87ec193f17e8dad82c9994ba7a58e975d364bAlan Viverette    protected boolean verifyDrawable(@NonNull Drawable who) {
235690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        return super.verifyDrawable(who) || who == mRemoteIndicator;
236690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
237690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
238690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
239690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void jumpDrawablesToCurrentState() {
240690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        super.jumpDrawablesToCurrentState();
2410abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
2420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mRemoteIndicator != null) {
2430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mRemoteIndicator.jumpToCurrentState();
2440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
245690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
246690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
247690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
248690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void setVisibility(int visibility) {
249690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        super.setVisibility(visibility);
2500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
251690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        if (mRemoteIndicator != null) {
252690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            mRemoteIndicator.setVisible(getVisibility() == VISIBLE, false);
253690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
254690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
255690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
256690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
257101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    public void onAttachedToWindow() {
258101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        super.onAttachedToWindow();
2590abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
260101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        mAttachedToWindow = true;
261101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        if (mRouteTypes != 0) {
2620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mRouter.addCallback(mRouteTypes, mCallback,
26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown                    MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
264101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        }
2650abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        refreshRoute();
266101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    }
267101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich
268101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    @Override
269101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    public void onDetachedFromWindow() {
2700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mAttachedToWindow = false;
271101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        if (mRouteTypes != 0) {
2720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mRouter.removeCallback(mCallback);
273101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        }
2740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
275101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich        super.onDetachedFromWindow();
276101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    }
277101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich
278101c44915259fb76d14f917712a51fb0b08a7594Jack Palevich    @Override
279690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
280690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
281690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
282690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
283690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
284690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
285de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang        final int width = Math.max(mMinWidth, mRemoteIndicator != null ?
286de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                mRemoteIndicator.getIntrinsicWidth() + getPaddingLeft() + getPaddingRight() : 0);
287de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang        final int height = Math.max(mMinHeight, mRemoteIndicator != null ?
288de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                mRemoteIndicator.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom() : 0);
289690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
290de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang        int measuredWidth;
291690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        switch (widthMode) {
292690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            case MeasureSpec.EXACTLY:
293de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                measuredWidth = widthSize;
294690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                break;
295690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            case MeasureSpec.AT_MOST:
296de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                measuredWidth = Math.min(widthSize, width);
297690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                break;
298690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            default:
299690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            case MeasureSpec.UNSPECIFIED:
300de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                measuredWidth = width;
301690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                break;
302690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
303690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
304de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang        int measuredHeight;
305690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        switch (heightMode) {
306690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            case MeasureSpec.EXACTLY:
307de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                measuredHeight = heightSize;
308690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                break;
309690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            case MeasureSpec.AT_MOST:
310de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                measuredHeight = Math.min(heightSize, height);
311690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                break;
312690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            default:
313690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            case MeasureSpec.UNSPECIFIED:
314de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang                measuredHeight = height;
315690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                break;
316690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
317690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
318de0d5d746fc030770b64858b64eac2b1c26e4ba6Dongwon Kang        setMeasuredDimension(measuredWidth, measuredHeight);
319690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
320690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
321690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
322690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    protected void onDraw(Canvas canvas) {
323690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        super.onDraw(canvas);
324690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
325690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        if (mRemoteIndicator == null) return;
326690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
327690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int left = getPaddingLeft();
328690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int right = getWidth() - getPaddingRight();
329690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int top = getPaddingTop();
330690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int bottom = getHeight() - getPaddingBottom();
331690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
332690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int drawWidth = mRemoteIndicator.getIntrinsicWidth();
333690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int drawHeight = mRemoteIndicator.getIntrinsicHeight();
334690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int drawLeft = left + (right - left - drawWidth) / 2;
335690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        final int drawTop = top + (bottom - top - drawHeight) / 2;
336690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
3370abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mRemoteIndicator.setBounds(drawLeft, drawTop,
3380abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                drawLeft + drawWidth, drawTop + drawHeight);
339690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        mRemoteIndicator.draw(canvas);
340690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
341690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
3420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private void refreshRoute() {
343d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
344d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes);
345d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        final boolean isConnecting = isRemote && route.isConnecting();
346d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        boolean needsRefresh = false;
347d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        if (mRemoteActive != isRemote) {
348d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            mRemoteActive = isRemote;
349d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            needsRefresh = true;
350d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        }
351d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        if (mIsConnecting != isConnecting) {
352d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            mIsConnecting = isConnecting;
353d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            needsRefresh = true;
354d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        }
35570e11e50eecfc8f1dfb76316d099e4331ebd28f7Adam Powell
356d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        if (needsRefresh) {
357d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            refreshDrawableState();
358d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        }
359d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        if (mAttachedToWindow) {
3600abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            setEnabled(mRouter.isRouteAvailable(mRouteTypes,
3610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                    MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
36270e11e50eecfc8f1dfb76316d099e4331ebd28f7Adam Powell        }
363d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        if (mRemoteIndicator != null
364d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
365d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
366d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            if (mAttachedToWindow) {
367d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) {
368d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                    curDrawable.start();
369d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                }
370d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            } else if (isRemote && !isConnecting) {
371d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                // When the route is already connected before the view is attached, show the last
372d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                // frame of the connected animation immediately.
373d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                if (curDrawable.isRunning()) {
374d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                    curDrawable.stop();
375d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                }
376d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren                curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1);
377d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren            }
378d9125cf9df3fd97082bb801f23758eec8f8f33e3yangren        }
379b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell    }
380b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell
3810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
382690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        @Override
3830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void onRouteAdded(MediaRouter router, RouteInfo info) {
3840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
385690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
386690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
387690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        @Override
3880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
3890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
390690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
391690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
392690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        @Override
3932ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
3940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
3952ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        }
3962ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell
3972ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell        @Override
3980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
3990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
400690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
401690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
402690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        @Override
4030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
4040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
405690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
406f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell
407f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell        @Override
408f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
409f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell                int index) {
4100abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
411f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell        }
412f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell
413f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell        @Override
414f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
4150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
416f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell        }
417690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
418690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell}
419