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