MediaRouteControllerDialog.java revision 692a547730bbc95ad277d5214ef3d786ce1e499f
1edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project/* 2edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Copyright 2018 The Android Open Source Project 3edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 4edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * you may not use this file except in compliance with the License. 6edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * You may obtain a copy of the License at 7edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 8edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 10edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * See the License for the specific language governing permissions and 14edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * limitations under the License. 15edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project */ 16edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 17edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectpackage com.android.support.mediarouter.app; 18edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 19edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE; 20edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY; 21edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_PAUSE; 22edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport static android.support.v4.media.session.PlaybackStateCompat.ACTION_STOP; 23c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopian 24c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopianimport android.app.PendingIntent; 25c5b2c0bf8007562536b822eb060fc54a01f8e08bMathias Agopianimport android.content.ContentResolver; 26edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.content.Context; 27d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopianimport android.content.res.Resources; 282adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFaddenimport android.graphics.Bitmap; 292b5dd4062dd951b2cafc4b80bd3a1813b013d71aMathias Agopianimport android.graphics.BitmapFactory; 302b5dd4062dd951b2cafc4b80bd3a1813b013d71aMathias Agopianimport android.graphics.Rect; 31a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raitaimport android.graphics.drawable.BitmapDrawable; 324cdc58f6840d15b4952149d2b345ec1f97d505bcRobert Carrimport android.net.Uri; 33edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.os.AsyncTask; 3428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wrightimport android.os.Bundle; 3528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wrightimport android.os.RemoteException; 3690ac799241f077a7b7e6c1875fd933864c8dd2a7Mathias Agopianimport android.os.SystemClock; 3767d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnarimport android.support.v4.media.MediaDescriptionCompat; 38c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stozaimport android.support.v4.media.MediaMetadataCompat; 39582270d69db94286a248bd829f1ae6f910d45124Jamie Gennisimport android.support.v4.media.session.MediaControllerCompat; 40134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennisimport android.support.v4.media.session.MediaSessionCompat; 419cce325fae8adcf7560a28eef394489f09bad74dMathias Agopianimport android.support.v4.media.session.PlaybackStateCompat; 42edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.support.v4.util.ObjectsCompat; 43edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.support.v4.view.accessibility.AccessibilityEventCompat; 44edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.support.v7.app.AlertDialog; 45edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.support.v7.graphics.Palette; 46edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 47edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.text.TextUtils; 48edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.util.Log; 49e2347b7953f70232541bdc1b392230d6231aaeb9Chih-Hung Hsiehimport android.view.KeyEvent; 50edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.LayoutInflater; 51edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.View; 52edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.View.MeasureSpec; 53edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.ViewGroup; 54d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport android.view.ViewTreeObserver; 55d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport android.view.accessibility.AccessibilityEvent; 567e27f05739c8a2655cf0f7faea35614ce0a50278Mathias Agopianimport android.view.accessibility.AccessibilityManager; 57edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.animation.AccelerateDecelerateInterpolator; 58edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.animation.AlphaAnimation; 59edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.animation.Animation; 60edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.animation.AnimationSet; 617e27f05739c8a2655cf0f7faea35614ce0a50278Mathias Agopianimport android.view.animation.AnimationUtils; 62edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.animation.Interpolator; 63edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport android.view.animation.Transformation; 641db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.view.animation.TranslateAnimation; 651db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.ArrayAdapter; 661db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.Button; 671db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.FrameLayout; 681db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.ImageButton; 691db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.ImageView; 701db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.LinearLayout; 711db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.RelativeLayout; 721db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.SeekBar; 731db73f66624e7d151710483dd58e03eed672f064Robert Carrimport android.widget.TextView; 748b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian 758b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopianimport com.android.media.update.R; 768b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopianimport com.android.support.mediarouter.media.MediaRouteSelector; 778b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopianimport com.android.support.mediarouter.media.MediaRouter; 78edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport com.android.support.mediarouter.app.OverlayListView.OverlayObject; 79edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 80edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectimport java.io.BufferedInputStream; 81d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.io.IOException; 82d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.io.InputStream; 83d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.net.URL; 84d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.net.URLConnection; 858b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopianimport java.util.ArrayList; 86d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.util.HashMap; 87d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.util.HashSet; 88d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.util.List; 89d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.util.Map; 90698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopianimport java.util.Set; 91d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stozaimport java.util.concurrent.TimeUnit; 92d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza 93b8d69a55f1c187a35ac41e69de63251f5501b6f4Jamie Gennis/** 94edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * This class implements the route controller dialog for {@link MediaRouter}. 95edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * <p> 96edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * This dialog allows the user to control or disconnect from the currently selected route. 97edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * </p> 98edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * 99edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * @see MediaRouteButton 100edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project * @see MediaRouteActionProvider 101edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project */ 102edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Projectpublic class MediaRouteControllerDialog extends AlertDialog { 10340482ff650751819d4104c10a30974838168438cChavi Weingarten // Tags should be less than 24 characters long (see docs for android.util.Log.isLoggable()) 10440482ff650751819d4104c10a30974838168438cChavi Weingarten static final String TAG = "MediaRouteCtrlDialog"; 10540482ff650751819d4104c10a30974838168438cChavi Weingarten static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 10640482ff650751819d4104c10a30974838168438cChavi Weingarten 1072a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian // Time to wait before updating the volume when the user lets go of the seek bar 1082a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian // to allow the route provider time to propagate the change and publish a new 1092a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian // route descriptor. 110c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza static final int VOLUME_UPDATE_DELAY_MILLIS = 500; 111d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza static final int CONNECTION_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(30L); 112d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza 113ae0608381b2b4699218febd6d45ad9d307544d55Robert Carr private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3; 114ae0608381b2b4699218febd6d45ad9d307544d55Robert Carr static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2; 115c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza static final int BUTTON_STOP_RES_ID = android.R.id.button1; 116c3ebe66b49cfba035e1fd0e160a13db38eb81b0eRiley Andrews 11740482ff650751819d4104c10a30974838168438cChavi Weingarten final MediaRouter mRouter; 11840482ff650751819d4104c10a30974838168438cChavi Weingarten private final MediaRouterCallback mCallback; 11940482ff650751819d4104c10a30974838168438cChavi Weingarten final MediaRouter.RouteInfo mRoute; 12040482ff650751819d4104c10a30974838168438cChavi Weingarten 12140482ff650751819d4104c10a30974838168438cChavi Weingarten Context mContext; 12240482ff650751819d4104c10a30974838168438cChavi Weingarten private boolean mCreated; 12340482ff650751819d4104c10a30974838168438cChavi Weingarten private boolean mAttachedToWindow; 12440482ff650751819d4104c10a30974838168438cChavi Weingarten 12540482ff650751819d4104c10a30974838168438cChavi Weingarten private int mDialogContentWidth; 12640482ff650751819d4104c10a30974838168438cChavi Weingarten 12740482ff650751819d4104c10a30974838168438cChavi Weingarten private View mCustomControlView; 12840482ff650751819d4104c10a30974838168438cChavi Weingarten 12940482ff650751819d4104c10a30974838168438cChavi Weingarten private Button mDisconnectButton; 13040482ff650751819d4104c10a30974838168438cChavi Weingarten private Button mStopCastingButton; 1312a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian private ImageButton mPlaybackControlButton; 1322a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian private ImageButton mCloseButton; 133a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw private MediaRouteExpandCollapseButton mGroupExpandCollapseButton; 13440482ff650751819d4104c10a30974838168438cChavi Weingarten 135578038fc49f83c4c8c4accdce49df404ecd6ad02Robert Carr private FrameLayout mExpandableAreaLayout; 136a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw private LinearLayout mDialogAreaLayout; 137a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw FrameLayout mDefaultControlLayout; 138a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw private FrameLayout mCustomControlLayout; 1397206d49b2963ce2e926a5f25fe94aca9c06471e6chaviw private ImageView mArtView; 1407206d49b2963ce2e926a5f25fe94aca9c06471e6chaviw private TextView mTitleView; 141578038fc49f83c4c8c4accdce49df404ecd6ad02Robert Carr private TextView mSubtitleView; 14240482ff650751819d4104c10a30974838168438cChavi Weingarten private TextView mRouteNameTextView; 14340482ff650751819d4104c10a30974838168438cChavi Weingarten 14440482ff650751819d4104c10a30974838168438cChavi Weingarten private boolean mVolumeControlEnabled = true; 14540482ff650751819d4104c10a30974838168438cChavi Weingarten // Layout for media controllers including play/pause button and the main volume slider. 14640482ff650751819d4104c10a30974838168438cChavi Weingarten private LinearLayout mMediaMainControlLayout; 14740482ff650751819d4104c10a30974838168438cChavi Weingarten private RelativeLayout mPlaybackControlLayout; 14840482ff650751819d4104c10a30974838168438cChavi Weingarten private LinearLayout mVolumeControlLayout; 14940482ff650751819d4104c10a30974838168438cChavi Weingarten private View mDividerView; 15040482ff650751819d4104c10a30974838168438cChavi Weingarten 15140482ff650751819d4104c10a30974838168438cChavi Weingarten OverlayListView mVolumeGroupList; 15240482ff650751819d4104c10a30974838168438cChavi Weingarten VolumeGroupAdapter mVolumeGroupAdapter; 15340482ff650751819d4104c10a30974838168438cChavi Weingarten private List<MediaRouter.RouteInfo> mGroupMemberRoutes; 15440482ff650751819d4104c10a30974838168438cChavi Weingarten Set<MediaRouter.RouteInfo> mGroupMemberRoutesAdded; 15540482ff650751819d4104c10a30974838168438cChavi Weingarten private Set<MediaRouter.RouteInfo> mGroupMemberRoutesRemoved; 15640482ff650751819d4104c10a30974838168438cChavi Weingarten Set<MediaRouter.RouteInfo> mGroupMemberRoutesAnimatingWithBitmap; 157a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw SeekBar mVolumeSlider; 158a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw VolumeChangeListener mVolumeChangeListener; 159582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis MediaRouter.RouteInfo mRouteInVolumeSliderTouched; 1602adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden private int mVolumeGroupListItemIconSize; 161134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private int mVolumeGroupListItemHeight; 162134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private int mVolumeGroupListMaxHeight; 163134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private final int mVolumeGroupListPaddingTop; 164134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis Map<MediaRouter.RouteInfo, SeekBar> mVolumeSliderMap; 165134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis 166134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis MediaControllerCompat mMediaController; 167e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block MediaControllerCallback mControllerCallback; 168134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis PlaybackStateCompat mState; 169134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis MediaDescriptionCompat mDescription; 170134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis 1712ea926bda2fa30f3ba8d0ed2d2395a8ada952e6eMarco Nelissen FetchArtTask mFetchArtTask; 172134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis Bitmap mArtIconBitmap; 173e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block Uri mArtIconUri; 174582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis boolean mArtIconIsLoaded; 175134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis Bitmap mArtIconLoadedBitmap; 176134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis int mArtIconBackgroundColor; 177134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis 178134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis boolean mHasPendingUpdate; 179134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis boolean mPendingUpdateAnimationNeeded; 180e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block 181582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis boolean mIsGroupExpanded; 182134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis boolean mIsGroupListAnimating; 183134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis boolean mIsGroupListAnimationPending; 184134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis int mGroupListAnimationDurationMs; 185134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private int mGroupListFadeInDurationMs; 186134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private int mGroupListFadeOutDurationMs; 187e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block 188582270d69db94286a248bd829f1ae6f910d45124Jamie Gennis private Interpolator mInterpolator; 189134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private Interpolator mLinearOutSlowInInterpolator; 190134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private Interpolator mFastOutSlowInInterpolator; 191134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis private Interpolator mAccelerateDecelerateInterpolator; 192134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis 193d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian final AccessibilityManager mAccessibilityManager; 1946b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 1956b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson Runnable mGroupListFadeInAnimation = new Runnable() { 1966b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson @Override 1976b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson public void run() { 1986b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson startGroupListFadeInAnimation(); 1996b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 2006b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson }; 2016b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 2026b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson public MediaRouteControllerDialog(Context context) { 2036b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson this(context, 0); 2046b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 2056b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 2066b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson public MediaRouteControllerDialog(Context context, int theme) { 2076b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson super(context = MediaRouterThemeHelper.createThemedDialogContext(context, theme, true), 2086b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson MediaRouterThemeHelper.createThemedDialogStyle(context)); 2096b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mContext = getContext(); 2106b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 2116b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mControllerCallback = new MediaControllerCallback(); 2126b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mRouter = MediaRouter.getInstance(mContext); 2136b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mCallback = new MediaRouterCallback(); 2146b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mRoute = mRouter.getSelectedRoute(); 2156b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson setMediaSession(mRouter.getMediaSessionToken()); 2166b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mVolumeGroupListPaddingTop = mContext.getResources().getDimensionPixelSize( 2176b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson R.dimen.mr_controller_volume_group_list_padding_top); 2186b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mAccessibilityManager = 2196b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); 2206b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson if (android.os.Build.VERSION.SDK_INT >= 21) { 2216b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 2226b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson R.interpolator.mr_linear_out_slow_in); 2236b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, 2246b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson R.interpolator.mr_fast_out_slow_in); 2256b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 2266b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mAccelerateDecelerateInterpolator = new AccelerateDecelerateInterpolator(); 2276b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 2286b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 2296b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson /** 2306b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson * Gets the route that this dialog is controlling. 2316b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson */ 2326b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson public MediaRouter.RouteInfo getRoute() { 2336b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson return mRoute; 2346b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 2356b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 2366b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson private MediaRouter.RouteGroup getGroup() { 2376b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson if (mRoute instanceof MediaRouter.RouteGroup) { 238b1e2f8deb38353e4bcc9d3ef06bc15bd5e417425Jorim Jaggi return (MediaRouter.RouteGroup) mRoute; 239d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian } 240d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian return null; 241d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian } 242d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian 243d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian /** 244d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * Provides the subclass an opportunity to create a view that will replace the default media 245d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * controls for the currently playing content. 246d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * 247b1e2f8deb38353e4bcc9d3ef06bc15bd5e417425Jorim Jaggi * @param savedInstanceState The dialog's saved instance state. 248d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * @return The media control view, or null if none. 249d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian */ 250d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian public View onCreateMediaControlView(Bundle savedInstanceState) { 251d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian return null; 252e6f43ddce78d6846af12550ff9193c5c6fe5844bSteve Block } 253d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian 254d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian /** 255d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}. 256d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * 257d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian * @return The media control view, or null if none. 258d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian */ 2598e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross public View getMediaControlView() { 260dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis return mCustomControlView; 261e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian } 262e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian 263e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian /** 2648dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden * Sets whether to enable the volume slider and volume control using the volume keys 265dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis * when the route supports it. 266e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian * <p> 267e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian * The default value is true. 268e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian * </p> 269e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian */ 2706c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall public void setVolumeControlEnabled(boolean enable) { 2716c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall if (mVolumeControlEnabled != enable) { 2726c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall mVolumeControlEnabled = enable; 2736c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall if (mCreated) { 2746c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall update(false); 2756c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall } 2766c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall } 2776c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall } 278e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian 279e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian /** 280e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian * Returns whether to enable the volume slider and volume control using the volume keys 281e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian * when the route supports it. 282e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian */ 283e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian public boolean isVolumeControlEnabled() { 284e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian return mVolumeControlEnabled; 285e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian } 286e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian 2872c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani /** 2888e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross * Set the session to use for metadata and transport controls. The dialog 2898e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross * will listen to changes on this session and update the UI automatically in 2908e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross * response to changes. 291c01a79d77b829e7de86ef137694e8ad708470ca1Andy McFadden * 2922c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani * @param sessionToken The token for the session to use. 2932c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani */ 2948e533069e5721e55cb9768e140e16546c3a4a8b6Colin Cross private void setMediaSession(MediaSessionCompat.Token sessionToken) { 2953094df359d1e6e2ae8ca4e935cc093f563804c96Mathias Agopian if (mMediaController != null) { 2967f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mMediaController.unregisterCallback(mControllerCallback); 2977f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mMediaController = null; 298c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian } 299c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian if (sessionToken == null) { 300c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian return; 3019d4e3d2f42e93e2d12bacabe97d307d30c3c20ddJeff Brown } 3027f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza if (!mAttachedToWindow) { 3037f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza return; 3047f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 305d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza try { 3067f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mMediaController = new MediaControllerCompat(mContext, sessionToken); 3077f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } catch (RemoteException e) { 3087f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza Log.e(TAG, "Error creating media controller in setMediaSession.", e); 3097f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 3107f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza if (mMediaController != null) { 3117f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mMediaController.registerCallback(mControllerCallback); 3127f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 3137f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza MediaMetadataCompat metadata = mMediaController == null ? null 3147f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza : mMediaController.getMetadata(); 3157f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mDescription = metadata == null ? null : metadata.getDescription(); 3167f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mState = mMediaController == null ? null : mMediaController.getPlaybackState(); 31767d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar updateArtIconIfNeeded(); 31867d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar update(false); 31967d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar } 32067d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar 32167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar /** 32267d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar * Gets the session to use for metadata and transport controls. 32367d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar * 32467d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar * @return The token for the session to use or null if none. 32567d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar */ 32667d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar public MediaSessionCompat.Token getMediaSession() { 32767d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar return mMediaController == null ? null : mMediaController.getSessionToken(); 32867d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar } 32967d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar 33067d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar @Override 33167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar protected void onCreate(Bundle savedInstanceState) { 33267d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar super.onCreate(savedInstanceState); 3337f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 3347f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza getWindow().setBackgroundDrawableResource(android.R.color.transparent); 3357f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza setContentView(R.layout.mr_controller_material_dialog_b); 3367f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 3377f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza // Remove the neutral button. 3387f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE); 3397f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 3407f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza ClickListener listener = new ClickListener(); 3417f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 3427f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mExpandableAreaLayout = findViewById(R.id.mr_expandable_area); 3437f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mExpandableAreaLayout.setOnClickListener(new View.OnClickListener() { 3447f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza @Override 3457f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza public void onClick(View v) { 3467f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza dismiss(); 3477f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 3487f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza }); 349c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian mDialogAreaLayout = findViewById(R.id.mr_dialog_area); 350c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian mDialogAreaLayout.setOnClickListener(new View.OnClickListener() { 351d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav @Override 35228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright public void onClick(View v) { 353a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin // Eat unhandled touch events. 35428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 35528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright }); 35628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright int color = MediaRouterThemeHelper.getButtonTextColor(mContext); 35728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mDisconnectButton = findViewById(BUTTON_DISCONNECT_RES_ID); 35828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mDisconnectButton.setText(R.string.mr_controller_disconnect); 35928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mDisconnectButton.setTextColor(color); 36028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mDisconnectButton.setOnClickListener(listener); 36128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 36228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mStopCastingButton = findViewById(BUTTON_STOP_RES_ID); 36328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mStopCastingButton.setText(R.string.mr_controller_stop_casting); 36428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mStopCastingButton.setTextColor(color); 36528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mStopCastingButton.setOnClickListener(listener); 36628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 36728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mRouteNameTextView = findViewById(R.id.mr_name); 36828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mCloseButton = findViewById(R.id.mr_close); 36928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mCloseButton.setOnClickListener(listener); 37028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mCustomControlLayout = findViewById(R.id.mr_custom_control); 37128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mDefaultControlLayout = findViewById(R.id.mr_default_control); 37228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 37328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright // Start the session activity when a content item (album art, title or subtitle) is clicked. 37428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright View.OnClickListener onClickListener = new View.OnClickListener() { 37528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright @Override 376a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin public void onClick(View v) { 37728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright if (mMediaController != null) { 37828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright PendingIntent pi = mMediaController.getSessionActivity(); 37928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright if (pi != null) { 38028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright try { 38128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright pi.send(); 382a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin dismiss(); 38328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } catch (PendingIntent.CanceledException e) { 38428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright Log.e(TAG, pi + " was not sent, it had been canceled."); 38528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 38628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 387a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin } 38828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 38928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright }; 39028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mArtView = findViewById(R.id.mr_art); 39128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mArtView.setOnClickListener(onClickListener); 392a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin findViewById(R.id.mr_control_title_container).setOnClickListener(onClickListener); 39328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 39428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mMediaMainControlLayout = findViewById(R.id.mr_media_main_control); 39528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mDividerView = findViewById(R.id.mr_control_divider); 39628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 397a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mPlaybackControlLayout = findViewById(R.id.mr_playback_control); 39828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mTitleView = findViewById(R.id.mr_control_title); 399a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mSubtitleView = findViewById(R.id.mr_control_subtitle); 40028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mPlaybackControlButton = findViewById(R.id.mr_control_playback_ctrl); 40128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mPlaybackControlButton.setOnClickListener(listener); 40228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 403a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mVolumeControlLayout = findViewById(R.id.mr_volume_control); 40428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeControlLayout.setVisibility(View.GONE); 40528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeSlider = findViewById(R.id.mr_volume_slider); 40628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeSlider.setTag(mRoute); 40728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeChangeListener = new VolumeChangeListener(); 40828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener); 40928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 41028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupList = findViewById(R.id.mr_volume_group_list); 41128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutes = new ArrayList<MediaRouter.RouteInfo>(); 41228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupAdapter = new VolumeGroupAdapter(mVolumeGroupList.getContext(), 41328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutes); 41428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupList.setAdapter(mVolumeGroupAdapter); 415a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mGroupMemberRoutesAnimatingWithBitmap = new HashSet<>(); 41628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 41728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright MediaRouterThemeHelper.setMediaControlsBackgroundColor(mContext, 41828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mMediaMainControlLayout, mVolumeGroupList, getGroup() != null); 41928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright MediaRouterThemeHelper.setVolumeSliderColor(mContext, 42028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright (MediaRouteVolumeSlider) mVolumeSlider, mMediaMainControlLayout); 42128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeSliderMap = new HashMap<>(); 42228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeSliderMap.put(mRoute, mVolumeSlider); 42328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 42428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupExpandCollapseButton = 42528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright findViewById(R.id.mr_group_expand_collapse); 42628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() { 42728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright @Override 428d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav public void onClick(View v) { 429d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav mIsGroupExpanded = !mIsGroupExpanded; 430d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav if (mIsGroupExpanded) { 431d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav mVolumeGroupList.setVisibility(View.VISIBLE); 432d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav } 433d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav loadInterpolator(); 434d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav updateLayoutHeight(true); 435d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav } 436d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav }); 437d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav loadInterpolator(); 438d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav mGroupListAnimationDurationMs = mContext.getResources().getInteger( 439d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav R.integer.mr_controller_volume_group_list_animation_duration_ms); 440d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav mGroupListFadeInDurationMs = mContext.getResources().getInteger( 441d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav R.integer.mr_controller_volume_group_list_fade_in_duration_ms); 442c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza mGroupListFadeOutDurationMs = mContext.getResources().getInteger( 443c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza R.integer.mr_controller_volume_group_list_fade_out_duration_ms); 444c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza 445c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza mCustomControlView = onCreateMediaControlView(savedInstanceState); 446c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza if (mCustomControlView != null) { 447c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza mCustomControlLayout.addView(mCustomControlView); 448c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza mCustomControlLayout.setVisibility(View.VISIBLE); 449c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza } 450c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza mCreated = true; 451c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza updateLayout(); 452c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza } 453c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza 454c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza /** 455c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza * Sets the width of the dialog. Also called when configuration changes. 456c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza */ 457c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza void updateLayout() { 458c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza int width = MediaRouteDialogHelper.getDialogWidth(mContext); 459c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza getWindow().setLayout(width, ViewGroup.LayoutParams.WRAP_CONTENT); 460e1f5e6fd7ad07778fa4381059852a94567d77dfcMathias Agopian 461c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza View decorView = getWindow().getDecorView(); 462c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza mDialogContentWidth = width - decorView.getPaddingLeft() - decorView.getPaddingRight(); 463c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza 464c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju Resources res = mContext.getResources(); 465c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mVolumeGroupListItemIconSize = res.getDimensionPixelSize( 466c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju R.dimen.mr_controller_volume_group_list_item_icon_size); 467c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mVolumeGroupListItemHeight = res.getDimensionPixelSize( 468c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju R.dimen.mr_controller_volume_group_list_item_height); 469c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mVolumeGroupListMaxHeight = res.getDimensionPixelSize( 470c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju R.dimen.mr_controller_volume_group_list_max_height); 471c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju 472c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju // Fetch art icons again for layout changes to resize it accordingly 473c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mArtIconBitmap = null; 474c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mArtIconUri = null; 475c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju updateArtIconIfNeeded(); 476c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju update(false); 477c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 478c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju 479c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju @Override 480c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju public void onAttachedToWindow() { 481c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju super.onAttachedToWindow(); 482c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mAttachedToWindow = true; 483c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju 484c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback, 485c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS); 486c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju setMediaSession(mRouter.getMediaSessionToken()); 487c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 488c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju 489c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju @Override 490c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju public void onDetachedFromWindow() { 491c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mRouter.removeCallback(mCallback); 492c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju setMediaSession(null); 493c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mAttachedToWindow = false; 494c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju super.onDetachedFromWindow(); 495c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 496c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju 497c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju @Override 498c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju public boolean onKeyDown(int keyCode, KeyEvent event) { 499c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN 500c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 501c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1); 502c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju return true; 503c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 504c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju return super.onKeyDown(keyCode, event); 505c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 506a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita 507a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita @Override 508a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita public boolean onKeyUp(int keyCode, KeyEvent event) { 509a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN 510a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 511a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita return true; 512a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita } 513a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita return super.onKeyUp(keyCode, event); 514a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita } 515a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita 516a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita void update(boolean animate) { 517a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita // Defer dialog updates if a user is adjusting a volume in the list 518a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita if (mRouteInVolumeSliderTouched != null) { 519a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita mHasPendingUpdate = true; 520a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita mPendingUpdateAnimationNeeded |= animate; 521a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita return; 522a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita } 523a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita mHasPendingUpdate = false; 524a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita mPendingUpdateAnimationNeeded = false; 525a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita if (!mRoute.isSelected() || mRoute.isDefaultOrBluetooth()) { 526a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita dismiss(); 527a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita return; 528a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita } 529a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita if (!mCreated) { 530a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita return; 531a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita } 532a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita 533a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita mRouteNameTextView.setText(mRoute.getName()); 534a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE); 535a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita if (mCustomControlView == null && mArtIconIsLoaded) { 536edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (isBitmapRecycled(mArtIconLoadedBitmap)) { 537edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project Log.w(TAG, "Can't set artwork image with recycled bitmap: " + mArtIconLoadedBitmap); 538d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza } else { 539d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza mArtView.setImageBitmap(mArtIconLoadedBitmap); 540d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza mArtView.setBackgroundColor(mArtIconBackgroundColor); 541d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza } 542edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project clearLoadedBitmap(); 543edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project } 544edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project updateVolumeControlLayout(); 545edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project updatePlaybackControlLayout(); 546edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project updateLayoutHeight(animate); 547edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project } 548edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project 549edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project private boolean isBitmapRecycled(Bitmap bitmap) { 550edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project return bitmap != null && bitmap.isRecycled(); 55183c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian } 5522ea926bda2fa30f3ba8d0ed2d2395a8ada952e6eMarco Nelissen 553edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project private boolean canShowPlaybackControlLayout() { 5546c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall return mCustomControlView == null && (mDescription != null || mState != null); 5556c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall } 5561db73f66624e7d151710483dd58e03eed672f064Robert Carr 5571db73f66624e7d151710483dd58e03eed672f064Robert Carr /** 5581db73f66624e7d151710483dd58e03eed672f064Robert Carr * Returns the height of main media controller which includes playback control and master 5591db73f66624e7d151710483dd58e03eed672f064Robert Carr * volume control. 5601db73f66624e7d151710483dd58e03eed672f064Robert Carr */ 5611db73f66624e7d151710483dd58e03eed672f064Robert Carr private int getMainControllerHeight(boolean showPlaybackControl) { 5621db73f66624e7d151710483dd58e03eed672f064Robert Carr int height = 0; 5631db73f66624e7d151710483dd58e03eed672f064Robert Carr if (showPlaybackControl || mVolumeControlLayout.getVisibility() == View.VISIBLE) { 564698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian height += mMediaMainControlLayout.getPaddingTop() 56583c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian + mMediaMainControlLayout.getPaddingBottom(); 566d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza if (showPlaybackControl) { 567d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza height += mPlaybackControlLayout.getMeasuredHeight(); 5688afa1c4ab86d724feb7716e153b7835385534590Michael Lentine } 5698afa1c4ab86d724feb7716e153b7835385534590Michael Lentine if (mVolumeControlLayout.getVisibility() == View.VISIBLE) { 5708afa1c4ab86d724feb7716e153b7835385534590Michael Lentine height += mVolumeControlLayout.getMeasuredHeight(); 571698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian } 572698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian if (showPlaybackControl && mVolumeControlLayout.getVisibility() == View.VISIBLE) { 573698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian height += mDividerView.getMeasuredHeight(); 574d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza } 5758afa1c4ab86d724feb7716e153b7835385534590Michael Lentine } 5768afa1c4ab86d724feb7716e153b7835385534590Michael Lentine return height; 5778afa1c4ab86d724feb7716e153b7835385534590Michael Lentine } 578698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian 579698c0873cf2e07bdc7fd1e72169aee2a19fa40d7Mathias Agopian private void updateMediaControlVisibility(boolean canShowPlaybackControlLayout) { 580d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza // TODO: Update the top and bottom padding of the control layout according to the display 581d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza // height. 5828afa1c4ab86d724feb7716e153b7835385534590Michael Lentine mDividerView.setVisibility((mVolumeControlLayout.getVisibility() == View.VISIBLE 5838afa1c4ab86d724feb7716e153b7835385534590Michael Lentine && canShowPlaybackControlLayout) ? View.VISIBLE : View.GONE); 5848afa1c4ab86d724feb7716e153b7835385534590Michael Lentine mMediaMainControlLayout.setVisibility((mVolumeControlLayout.getVisibility() == View.GONE 5858b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian && !canShowPlaybackControlLayout) ? View.GONE : View.VISIBLE); 5868b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian } 5878b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian 588d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza void updateLayoutHeight(final boolean animate) { 5898afa1c4ab86d724feb7716e153b7835385534590Michael Lentine // We need to defer the update until the first layout has occurred, as we don't yet know the 5908afa1c4ab86d724feb7716e153b7835385534590Michael Lentine // overall visible display size in which the window this view is attached to has been 5918afa1c4ab86d724feb7716e153b7835385534590Michael Lentine // positioned in. 5928b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian mDefaultControlLayout.requestLayout(); 5938b33f032327f8de0dcc0e6d0d43ed80f834b51f6Mathias Agopian ViewTreeObserver observer = mDefaultControlLayout.getViewTreeObserver(); 594d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 595d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza @Override 596d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza public void onGlobalLayout() { 5976c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall mDefaultControlLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this); 5986c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall if (mIsGroupListAnimating) { 599edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project mIsGroupListAnimationPending = true; 60083c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian } else { 601edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project updateLayoutHeightInternal(animate); 6026c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall } 6036c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall } 6042a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian }); 6052a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian } 6062a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian 60740482ff650751819d4104c10a30974838168438cChavi Weingarten /** 60860d6922a011fe18c111b8d30fb6ef1f655b6b15ePablo Ceballos * Updates the height of views and hide artwork or metadata if space is limited. 609c18790018be5d7ea7061ccbc81f3044e74adc823Dan Stoza */ 610d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza void updateLayoutHeightInternal(boolean animate) { 611d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza // Measure the size of widgets and get the height of main components. 612ae0608381b2b4699218febd6d45ad9d307544d55Robert Carr int oldHeight = getLayoutHeight(mMediaMainControlLayout); 613ae0608381b2b4699218febd6d45ad9d307544d55Robert Carr setLayoutHeight(mMediaMainControlLayout, ViewGroup.LayoutParams.MATCH_PARENT); 614c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza updateMediaControlVisibility(canShowPlaybackControlLayout()); 615d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza View decorView = getWindow().getDecorView(); 616c701401f8cec2e5309f8b57e2b97baced5093274Dan Stoza decorView.measure( 61740482ff650751819d4104c10a30974838168438cChavi Weingarten MeasureSpec.makeMeasureSpec(getWindow().getAttributes().width, MeasureSpec.EXACTLY), 61840482ff650751819d4104c10a30974838168438cChavi Weingarten MeasureSpec.UNSPECIFIED); 61940482ff650751819d4104c10a30974838168438cChavi Weingarten setLayoutHeight(mMediaMainControlLayout, oldHeight); 6202a9fc493dfdba67108e4335bb1fe931bc1e2a025Mathias Agopian int artViewHeight = 0; 62140482ff650751819d4104c10a30974838168438cChavi Weingarten if (mCustomControlView == null && mArtView.getDrawable() instanceof BitmapDrawable) { 62240482ff650751819d4104c10a30974838168438cChavi Weingarten Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap(); 62340482ff650751819d4104c10a30974838168438cChavi Weingarten if (art != null) { 6246c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall artViewHeight = getDesiredArtHeight(art.getWidth(), art.getHeight()); 6256c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall mArtView.setScaleType(art.getWidth() >= art.getHeight() 626a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw ? ImageView.ScaleType.FIT_XY : ImageView.ScaleType.FIT_CENTER); 627a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw } 628a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw } 62940482ff650751819d4104c10a30974838168438cChavi Weingarten int mainControllerHeight = getMainControllerHeight(canShowPlaybackControlLayout()); 6307206d49b2963ce2e926a5f25fe94aca9c06471e6chaviw int volumeGroupListCount = mGroupMemberRoutes.size(); 6317206d49b2963ce2e926a5f25fe94aca9c06471e6chaviw // Scale down volume group list items in landscape mode. 6327206d49b2963ce2e926a5f25fe94aca9c06471e6chaviw int expandedGroupListHeight = getGroup() == null ? 0 : 633578038fc49f83c4c8c4accdce49df404ecd6ad02Robert Carr mVolumeGroupListItemHeight * getGroup().getRoutes().size(); 634a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw if (volumeGroupListCount > 0) { 635578038fc49f83c4c8c4accdce49df404ecd6ad02Robert Carr expandedGroupListHeight += mVolumeGroupListPaddingTop; 636578038fc49f83c4c8c4accdce49df404ecd6ad02Robert Carr } 637a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw expandedGroupListHeight = Math.min(expandedGroupListHeight, mVolumeGroupListMaxHeight); 63840482ff650751819d4104c10a30974838168438cChavi Weingarten int visibleGroupListHeight = mIsGroupExpanded ? expandedGroupListHeight : 0; 63940482ff650751819d4104c10a30974838168438cChavi Weingarten 64040482ff650751819d4104c10a30974838168438cChavi Weingarten int desiredControlLayoutHeight = 641a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight; 642a76b271f0e14325fa0ebb98e1cac0a15adfea1cbchaviw Rect visibleRect = new Rect(); 643134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis decorView.getWindowVisibleDisplayFrame(visibleRect); 644134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis // Height of non-control views in decor view. 6452adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden // This includes title bar, button bar, and dialog's vertical padding which should be 6462adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden // always shown. 6472adaf04fab35cf47c824d74d901b54094e01ccd3Andy McFadden int nonControlViewHeight = mDialogAreaLayout.getMeasuredHeight() 648134f0422866e8985188ed10dfbdcb8e6c34b87f7Jamie Gennis - mDefaultControlLayout.getMeasuredHeight(); 6496c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall // Maximum allowed height for controls to fit screen. 6506c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall int maximumControlViewHeight = visibleRect.height() - nonControlViewHeight; 6516b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson 6526b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson // Show artwork if it fits the screen. 6536b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson if (mCustomControlView == null && artViewHeight > 0 6546b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson && desiredControlLayoutHeight <= maximumControlViewHeight) { 6556b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mArtView.setVisibility(View.VISIBLE); 6566b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson setLayoutHeight(mArtView, artViewHeight); 6576b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } else { 6586b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson if (getLayoutHeight(mVolumeGroupList) + mMediaMainControlLayout.getMeasuredHeight() 6596b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson >= mDefaultControlLayout.getMeasuredHeight()) { 6606b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mArtView.setVisibility(View.GONE); 6616b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 6626b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson artViewHeight = 0; 6636b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson desiredControlLayoutHeight = visibleGroupListHeight + mainControllerHeight; 6646b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } 6656b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson // Show the playback control if it fits the screen. 6666b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson if (canShowPlaybackControlLayout() 6676b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson && desiredControlLayoutHeight <= maximumControlViewHeight) { 6686b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson mPlaybackControlLayout.setVisibility(View.VISIBLE); 6696b376713907086c9642e7b7e66e51ddfa531b003Brian Anderson } else { 670d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian mPlaybackControlLayout.setVisibility(View.GONE); 671d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian } 672b1e2f8deb38353e4bcc9d3ef06bc15bd5e417425Jorim Jaggi updateMediaControlVisibility(mPlaybackControlLayout.getVisibility() == View.VISIBLE); 673b1e2f8deb38353e4bcc9d3ef06bc15bd5e417425Jorim Jaggi mainControllerHeight = getMainControllerHeight( 6742ea926bda2fa30f3ba8d0ed2d2395a8ada952e6eMarco Nelissen mPlaybackControlLayout.getVisibility() == View.VISIBLE); 675d0566bc26fcf6ca396118701fa11900b627f2c09Mathias Agopian desiredControlLayoutHeight = 6766c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall Math.max(artViewHeight, visibleGroupListHeight) + mainControllerHeight; 677e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian 678e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian // Limit the volume group list height to fit the screen. 6798dfa92fef9759a881e96ee58d59875d35023aab9Andy McFadden if (desiredControlLayoutHeight > maximumControlViewHeight) { 680dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis visibleGroupListHeight -= (desiredControlLayoutHeight - maximumControlViewHeight); 681dd3cb84cfbe8068790c6233b5829fae9c4a0ee93Jamie Gennis desiredControlLayoutHeight = maximumControlViewHeight; 682e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian } 683e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian // Update the layouts with the computed heights. 6846c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall mMediaMainControlLayout.clearAnimation(); 6856c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall mVolumeGroupList.clearAnimation(); 6866c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall mDefaultControlLayout.clearAnimation(); 6876c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall if (animate) { 6886c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall animateLayoutHeight(mMediaMainControlLayout, mainControllerHeight); 6896c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall animateLayoutHeight(mVolumeGroupList, visibleGroupListHeight); 6906c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall animateLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight); 691e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian } else { 692e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian setLayoutHeight(mMediaMainControlLayout, mainControllerHeight); 693e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian setLayoutHeight(mVolumeGroupList, visibleGroupListHeight); 694e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian setLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight); 695e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian } 696e57f292595bec48f65c8088b00ff6beea01217e9Mathias Agopian // Maximize the window size with a transparent layout in advance for smooth animation. 6976c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall setLayoutHeight(mExpandableAreaLayout, visibleRect.height()); 6987f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza rebuildVolumeGroupList(animate); 6997f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 7007f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 7017f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza void updateVolumeGroupItemHeight(View item) { 7027f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza LinearLayout container = (LinearLayout) item.findViewById(R.id.volume_item_container); 7037f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza setLayoutHeight(container, mVolumeGroupListItemHeight); 7047f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza View icon = item.findViewById(R.id.mr_volume_item_icon); 705d723bd7669b4fc88dc282d8bf8ba5ecb2849d22fDan Stoza ViewGroup.LayoutParams lp = icon.getLayoutParams(); 7067f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza lp.width = mVolumeGroupListItemIconSize; 7077f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza lp.height = mVolumeGroupListItemIconSize; 7087f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza icon.setLayoutParams(lp); 7097f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 7107f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 7117f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza private void animateLayoutHeight(final View view, int targetHeight) { 7127f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza final int startValue = getLayoutHeight(view); 71367d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar final int endValue = targetHeight; 71467d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar Animation anim = new Animation() { 71567d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar @Override 71667d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar protected void applyTransformation(float interpolatedTime, Transformation t) { 71767d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar int height = startValue - (int) ((startValue - endValue) * interpolatedTime); 71867d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar setLayoutHeight(view, height); 71967d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar } 72067d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar }; 72167d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar anim.setDuration(mGroupListAnimationDurationMs); 72267d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar if (android.os.Build.VERSION.SDK_INT >= 21) { 72367d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar anim.setInterpolator(mInterpolator); 72467d8bd66aaf04805cb8f2616ba964141b865e3b9Lajos Molnar } 7257f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza view.startAnimation(anim); 7267f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 7277f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 7287f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza void loadInterpolator() { 7297f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza if (android.os.Build.VERSION.SDK_INT >= 21) { 7307f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza mInterpolator = mIsGroupExpanded ? mLinearOutSlowInInterpolator 7317f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza : mFastOutSlowInInterpolator; 7327f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } else { 733c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian mInterpolator = mAccelerateDecelerateInterpolator; 7349d4e3d2f42e93e2d12bacabe97d307d30c3c20ddJeff Brown } 7357f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza } 7367f7da32569f8e0b3d383a40b95f8ac1d55afd801Dan Stoza 737c666cae2d5995097ec49a87e375e2afdd92802b7Mathias Agopian private void updateVolumeControlLayout() { 7386c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall if (isVolumeControlAvailable(mRoute)) { 7396c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall if (mVolumeControlLayout.getVisibility() == View.GONE) { 74028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeControlLayout.setVisibility(View.VISIBLE); 74128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeSlider.setMax(mRoute.getVolumeMax()); 742a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mVolumeSlider.setProgress(mRoute.getVolume()); 74328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupExpandCollapseButton.setVisibility(getGroup() == null ? View.GONE 74428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright : View.VISIBLE); 74528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 74628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } else { 74728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeControlLayout.setVisibility(View.GONE); 74828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 74928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 75028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 75128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright private void rebuildVolumeGroupList(boolean animate) { 75228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright List<MediaRouter.RouteInfo> routes = getGroup() == null ? null : getGroup().getRoutes(); 75328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright if (routes == null) { 754a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mGroupMemberRoutes.clear(); 75528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupAdapter.notifyDataSetChanged(); 75628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } else if (MediaRouteDialogHelper.listUnorderedEquals(mGroupMemberRoutes, routes)) { 75728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupAdapter.notifyDataSetChanged(); 75828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } else { 75928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright HashMap<MediaRouter.RouteInfo, Rect> previousRouteBoundMap = animate 76028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright ? MediaRouteDialogHelper.getItemBoundMap(mVolumeGroupList, mVolumeGroupAdapter) 76128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright : null; 76228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright HashMap<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap = animate 76328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright ? MediaRouteDialogHelper.getItemBitmapMap(mContext, mVolumeGroupList, 76428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupAdapter) : null; 76528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutesAdded = 76628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright MediaRouteDialogHelper.getItemsAdded(mGroupMemberRoutes, routes); 767a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mGroupMemberRoutesRemoved = MediaRouteDialogHelper.getItemsRemoved(mGroupMemberRoutes, 76828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright routes); 76928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutes.addAll(0, mGroupMemberRoutesAdded); 77028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutes.removeAll(mGroupMemberRoutesRemoved); 77128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupAdapter.notifyDataSetChanged(); 77228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright if (animate && mIsGroupExpanded 77328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright && mGroupMemberRoutesAdded.size() + mGroupMemberRoutesRemoved.size() > 0) { 77428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright animateGroupListItems(previousRouteBoundMap, previousRouteBitmapMap); 77528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } else { 77628f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutesAdded = null; 77728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mGroupMemberRoutesRemoved = null; 77828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 77928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 78028f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright } 78128f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright 78228f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright private void animateGroupListItems(final Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap, 78328f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright final Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) { 78428f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupList.setEnabled(false); 78528f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright mVolumeGroupList.requestLayout(); 786a52f0295622a42849f5ef81c44589b816b2ccacbPeiyong Lin mIsGroupListAnimating = true; 78728f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver(); 78828f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 78928f24d0ab481bd9c6fd5618414fee694e837c5c6Michael Wright @Override 790d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav public void onGlobalLayout() { 791d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this); 792d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav animateGroupListItemsInternal(previousRouteBoundMap, previousRouteBitmapMap); 793d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav } 794d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav }); 795d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav } 796d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav 797d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav void animateGroupListItemsInternal( 798d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap, 799d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) { 800d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav if (mGroupMemberRoutesAdded == null || mGroupMemberRoutesRemoved == null) { 801d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav return; 802d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav } 803d85084b2b65828442eafaff9b811e9b6c9ca9fadSvetoslav int groupSizeDelta = mGroupMemberRoutesAdded.size() - mGroupMemberRoutesRemoved.size(); 8042c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani boolean listenerRegistered = false; 8052c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani Animation.AnimationListener listener = new Animation.AnimationListener() { 8062c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani @Override 8072c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani public void onAnimationStart(Animation animation) { 8082c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani mVolumeGroupList.startAnimationAll(); 8092c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani mVolumeGroupList.postDelayed(mGroupListFadeInAnimation, 8102c9b11f0291210c9b9513a1a0cce6afebd361b3bPrashant Malani mGroupListAnimationDurationMs); 811c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza } 812c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza 813c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza @Override 814c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza public void onAnimationEnd(Animation animation) { } 815c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza 816c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza @Override 817c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza public void onAnimationRepeat(Animation animation) { } 818c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza }; 819c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza 820c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza // Animate visible items from previous positions to current positions except routes added 821c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza // just before. Added routes will remain hidden until translate animation finishes. 822c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza int first = mVolumeGroupList.getFirstVisiblePosition(); 823c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { 824e1f5e6fd7ad07778fa4381059852a94567d77dfcMathias Agopian View view = mVolumeGroupList.getChildAt(i); 825c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza int position = first + i; 826c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); 827c4f471e75a8ec64ec34e3f2944a5a756215d0becDan Stoza Rect previousBounds = previousRouteBoundMap.get(route); 828c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju int currentTop = view.getTop(); 829c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju int previousTop = previousBounds != null ? previousBounds.top 830c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju : (currentTop + mVolumeGroupListItemHeight * groupSizeDelta); 831c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju AnimationSet animSet = new AnimationSet(true); 832c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { 833c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju previousTop = currentTop; 834c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f); 835c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju alphaAnim.setDuration(mGroupListFadeInDurationMs); 836c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju animSet.addAnimation(alphaAnim); 837c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 838c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju Animation translationAnim = new TranslateAnimation(0, 0, previousTop - currentTop, 0); 839c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju translationAnim.setDuration(mGroupListAnimationDurationMs); 840c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju animSet.addAnimation(translationAnim); 841c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju animSet.setFillAfter(true); 842c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju animSet.setFillEnabled(true); 843c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju animSet.setInterpolator(mInterpolator); 844c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju if (!listenerRegistered) { 845c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju listenerRegistered = true; 846c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju animSet.setAnimationListener(listener); 847c1ba5c4649554e744844b07cfe402b42fbe12ff3Sahil Dhanju } 848a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita view.clearAnimation(); 849a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita view.startAnimation(animSet); 850a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita previousRouteBoundMap.remove(route); 851a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita previousRouteBitmapMap.remove(route); 852a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita } 853a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita 854a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita // If a member route doesn't exist any longer, it can be either removed or moved out of the 855a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita // ListView layout boundary. In this case, use the previously captured bitmaps for 856a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita // animation. 857a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita for (Map.Entry<MediaRouter.RouteInfo, BitmapDrawable> item 858a099a24c93bfa599fc5c36a647e946c26f68514fKalle Raita : previousRouteBitmapMap.entrySet()) { 8596c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall final MediaRouter.RouteInfo route = item.getKey(); 86083c0446f27b9542d6c2e724817b2b2d8d1f55085Mathias Agopian final BitmapDrawable bitmap = item.getValue(); 8616c913be9ca95fd6b556d056e165a4ba6dc69795bJesse Hall final Rect bounds = previousRouteBoundMap.get(route); 862edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project OverlayObject object = null; 863edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project if (mGroupMemberRoutesRemoved.contains(route)) { 864edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project object = new OverlayObject(bitmap, bounds).setAlphaAnimation(1.0f, 0.0f) 865edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project .setDuration(mGroupListFadeOutDurationMs) 866edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project .setInterpolator(mInterpolator); 867edbf3b6af777b721cd2a1ef461947e51e88241e1The Android Open Source Project } else { 868 int deltaY = groupSizeDelta * mVolumeGroupListItemHeight; 869 object = new OverlayObject(bitmap, bounds).setTranslateYAnimation(deltaY) 870 .setDuration(mGroupListAnimationDurationMs) 871 .setInterpolator(mInterpolator) 872 .setAnimationEndListener(new OverlayObject.OnAnimationEndListener() { 873 @Override 874 public void onAnimationEnd() { 875 mGroupMemberRoutesAnimatingWithBitmap.remove(route); 876 mVolumeGroupAdapter.notifyDataSetChanged(); 877 } 878 }); 879 mGroupMemberRoutesAnimatingWithBitmap.add(route); 880 } 881 mVolumeGroupList.addOverlayObject(object); 882 } 883 } 884 885 void startGroupListFadeInAnimation() { 886 clearGroupListAnimation(true); 887 mVolumeGroupList.requestLayout(); 888 ViewTreeObserver observer = mVolumeGroupList.getViewTreeObserver(); 889 observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 890 @Override 891 public void onGlobalLayout() { 892 mVolumeGroupList.getViewTreeObserver().removeGlobalOnLayoutListener(this); 893 startGroupListFadeInAnimationInternal(); 894 } 895 }); 896 } 897 898 void startGroupListFadeInAnimationInternal() { 899 if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.size() != 0) { 900 fadeInAddedRoutes(); 901 } else { 902 finishAnimation(true); 903 } 904 } 905 906 void finishAnimation(boolean animate) { 907 mGroupMemberRoutesAdded = null; 908 mGroupMemberRoutesRemoved = null; 909 mIsGroupListAnimating = false; 910 if (mIsGroupListAnimationPending) { 911 mIsGroupListAnimationPending = false; 912 updateLayoutHeight(animate); 913 } 914 mVolumeGroupList.setEnabled(true); 915 } 916 917 private void fadeInAddedRoutes() { 918 Animation.AnimationListener listener = new Animation.AnimationListener() { 919 @Override 920 public void onAnimationStart(Animation animation) { } 921 922 @Override 923 public void onAnimationEnd(Animation animation) { 924 finishAnimation(true); 925 } 926 927 @Override 928 public void onAnimationRepeat(Animation animation) { } 929 }; 930 boolean listenerRegistered = false; 931 int first = mVolumeGroupList.getFirstVisiblePosition(); 932 for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { 933 View view = mVolumeGroupList.getChildAt(i); 934 int position = first + i; 935 MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); 936 if (mGroupMemberRoutesAdded.contains(route)) { 937 Animation alphaAnim = new AlphaAnimation(0.0f, 1.0f); 938 alphaAnim.setDuration(mGroupListFadeInDurationMs); 939 alphaAnim.setFillEnabled(true); 940 alphaAnim.setFillAfter(true); 941 if (!listenerRegistered) { 942 listenerRegistered = true; 943 alphaAnim.setAnimationListener(listener); 944 } 945 view.clearAnimation(); 946 view.startAnimation(alphaAnim); 947 } 948 } 949 } 950 951 void clearGroupListAnimation(boolean exceptAddedRoutes) { 952 int first = mVolumeGroupList.getFirstVisiblePosition(); 953 for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { 954 View view = mVolumeGroupList.getChildAt(i); 955 int position = first + i; 956 MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); 957 if (exceptAddedRoutes && mGroupMemberRoutesAdded != null 958 && mGroupMemberRoutesAdded.contains(route)) { 959 continue; 960 } 961 LinearLayout container = (LinearLayout) view.findViewById(R.id.volume_item_container); 962 container.setVisibility(View.VISIBLE); 963 AnimationSet animSet = new AnimationSet(true); 964 Animation alphaAnim = new AlphaAnimation(1.0f, 1.0f); 965 alphaAnim.setDuration(0); 966 animSet.addAnimation(alphaAnim); 967 Animation translationAnim = new TranslateAnimation(0, 0, 0, 0); 968 translationAnim.setDuration(0); 969 animSet.setFillAfter(true); 970 animSet.setFillEnabled(true); 971 view.clearAnimation(); 972 view.startAnimation(animSet); 973 } 974 mVolumeGroupList.stopAnimationAll(); 975 if (!exceptAddedRoutes) { 976 finishAnimation(false); 977 } 978 } 979 980 private void updatePlaybackControlLayout() { 981 if (canShowPlaybackControlLayout()) { 982 CharSequence title = mDescription == null ? null : mDescription.getTitle(); 983 boolean hasTitle = !TextUtils.isEmpty(title); 984 985 CharSequence subtitle = mDescription == null ? null : mDescription.getSubtitle(); 986 boolean hasSubtitle = !TextUtils.isEmpty(subtitle); 987 988 boolean showTitle = false; 989 boolean showSubtitle = false; 990 if (mRoute.getPresentationDisplayId() 991 != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) { 992 // The user is currently casting screen. 993 mTitleView.setText(R.string.mr_controller_casting_screen); 994 showTitle = true; 995 } else if (mState == null || mState.getState() == PlaybackStateCompat.STATE_NONE) { 996 // Show "No media selected" as we don't yet know the playback state. 997 mTitleView.setText(R.string.mr_controller_no_media_selected); 998 showTitle = true; 999 } else if (!hasTitle && !hasSubtitle) { 1000 mTitleView.setText(R.string.mr_controller_no_info_available); 1001 showTitle = true; 1002 } else { 1003 if (hasTitle) { 1004 mTitleView.setText(title); 1005 showTitle = true; 1006 } 1007 if (hasSubtitle) { 1008 mSubtitleView.setText(subtitle); 1009 showSubtitle = true; 1010 } 1011 } 1012 mTitleView.setVisibility(showTitle ? View.VISIBLE : View.GONE); 1013 mSubtitleView.setVisibility(showSubtitle ? View.VISIBLE : View.GONE); 1014 1015 if (mState != null) { 1016 boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING 1017 || mState.getState() == PlaybackStateCompat.STATE_PLAYING; 1018 Context playbackControlButtonContext = mPlaybackControlButton.getContext(); 1019 boolean visible = true; 1020 int iconDrawableAttr = 0; 1021 int iconDescResId = 0; 1022 if (isPlaying && isPauseActionSupported()) { 1023 iconDrawableAttr = R.attr.mediaRoutePauseDrawable; 1024 iconDescResId = R.string.mr_controller_pause; 1025 } else if (isPlaying && isStopActionSupported()) { 1026 iconDrawableAttr = R.attr.mediaRouteStopDrawable; 1027 iconDescResId = R.string.mr_controller_stop; 1028 } else if (!isPlaying && isPlayActionSupported()) { 1029 iconDrawableAttr = R.attr.mediaRoutePlayDrawable; 1030 iconDescResId = R.string.mr_controller_play; 1031 } else { 1032 visible = false; 1033 } 1034 mPlaybackControlButton.setVisibility(visible ? View.VISIBLE : View.GONE); 1035 if (visible) { 1036 mPlaybackControlButton.setImageResource( 1037 MediaRouterThemeHelper.getThemeResource( 1038 playbackControlButtonContext, iconDrawableAttr)); 1039 mPlaybackControlButton.setContentDescription( 1040 playbackControlButtonContext.getResources() 1041 .getText(iconDescResId)); 1042 } 1043 } 1044 } 1045 } 1046 1047 private boolean isPlayActionSupported() { 1048 return (mState.getActions() & (ACTION_PLAY | ACTION_PLAY_PAUSE)) != 0; 1049 } 1050 1051 private boolean isPauseActionSupported() { 1052 return (mState.getActions() & (ACTION_PAUSE | ACTION_PLAY_PAUSE)) != 0; 1053 } 1054 1055 private boolean isStopActionSupported() { 1056 return (mState.getActions() & ACTION_STOP) != 0; 1057 } 1058 1059 boolean isVolumeControlAvailable(MediaRouter.RouteInfo route) { 1060 return mVolumeControlEnabled && route.getVolumeHandling() 1061 == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE; 1062 } 1063 1064 private static int getLayoutHeight(View view) { 1065 return view.getLayoutParams().height; 1066 } 1067 1068 static void setLayoutHeight(View view, int height) { 1069 ViewGroup.LayoutParams lp = view.getLayoutParams(); 1070 lp.height = height; 1071 view.setLayoutParams(lp); 1072 } 1073 1074 private static boolean uriEquals(Uri uri1, Uri uri2) { 1075 if (uri1 != null && uri1.equals(uri2)) { 1076 return true; 1077 } else if (uri1 == null && uri2 == null) { 1078 return true; 1079 } 1080 return false; 1081 } 1082 1083 /** 1084 * Returns desired art height to fit into controller dialog. 1085 */ 1086 int getDesiredArtHeight(int originalWidth, int originalHeight) { 1087 if (originalWidth >= originalHeight) { 1088 // For landscape art, fit width to dialog width. 1089 return (int) ((float) mDialogContentWidth * originalHeight / originalWidth + 0.5f); 1090 } 1091 // For portrait art, fit height to 16:9 ratio case's height. 1092 return (int) ((float) mDialogContentWidth * 9 / 16 + 0.5f); 1093 } 1094 1095 void updateArtIconIfNeeded() { 1096 if (mCustomControlView != null || !isIconChanged()) { 1097 return; 1098 } 1099 if (mFetchArtTask != null) { 1100 mFetchArtTask.cancel(true); 1101 } 1102 mFetchArtTask = new FetchArtTask(); 1103 mFetchArtTask.execute(); 1104 } 1105 1106 /** 1107 * Clear the bitmap loaded by FetchArtTask. Will be called after the loaded bitmaps are applied 1108 * to artwork, or no longer valid. 1109 */ 1110 void clearLoadedBitmap() { 1111 mArtIconIsLoaded = false; 1112 mArtIconLoadedBitmap = null; 1113 mArtIconBackgroundColor = 0; 1114 } 1115 1116 /** 1117 * Returns whether a new art image is different from an original art image. Compares 1118 * Bitmap objects first, and then compares URIs only if bitmap is unchanged with 1119 * a null value. 1120 */ 1121 private boolean isIconChanged() { 1122 Bitmap newBitmap = mDescription == null ? null : mDescription.getIconBitmap(); 1123 Uri newUri = mDescription == null ? null : mDescription.getIconUri(); 1124 Bitmap oldBitmap = mFetchArtTask == null ? mArtIconBitmap : mFetchArtTask.getIconBitmap(); 1125 Uri oldUri = mFetchArtTask == null ? mArtIconUri : mFetchArtTask.getIconUri(); 1126 if (oldBitmap != newBitmap) { 1127 return true; 1128 } else if (oldBitmap == null && !uriEquals(oldUri, newUri)) { 1129 return true; 1130 } 1131 return false; 1132 } 1133 1134 private final class MediaRouterCallback extends MediaRouter.Callback { 1135 MediaRouterCallback() { 1136 } 1137 1138 @Override 1139 public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) { 1140 update(false); 1141 } 1142 1143 @Override 1144 public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) { 1145 update(true); 1146 } 1147 1148 @Override 1149 public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) { 1150 SeekBar volumeSlider = mVolumeSliderMap.get(route); 1151 int volume = route.getVolume(); 1152 if (DEBUG) { 1153 Log.d(TAG, "onRouteVolumeChanged(), route.getVolume:" + volume); 1154 } 1155 if (volumeSlider != null && mRouteInVolumeSliderTouched != route) { 1156 volumeSlider.setProgress(volume); 1157 } 1158 } 1159 } 1160 1161 private final class MediaControllerCallback extends MediaControllerCompat.Callback { 1162 MediaControllerCallback() { 1163 } 1164 1165 @Override 1166 public void onSessionDestroyed() { 1167 if (mMediaController != null) { 1168 mMediaController.unregisterCallback(mControllerCallback); 1169 mMediaController = null; 1170 } 1171 } 1172 1173 @Override 1174 public void onPlaybackStateChanged(PlaybackStateCompat state) { 1175 mState = state; 1176 update(false); 1177 } 1178 1179 @Override 1180 public void onMetadataChanged(MediaMetadataCompat metadata) { 1181 mDescription = metadata == null ? null : metadata.getDescription(); 1182 updateArtIconIfNeeded(); 1183 update(false); 1184 } 1185 } 1186 1187 private final class ClickListener implements View.OnClickListener { 1188 ClickListener() { 1189 } 1190 1191 @Override 1192 public void onClick(View v) { 1193 int id = v.getId(); 1194 if (id == BUTTON_STOP_RES_ID || id == BUTTON_DISCONNECT_RES_ID) { 1195 if (mRoute.isSelected()) { 1196 mRouter.unselect(id == BUTTON_STOP_RES_ID ? 1197 MediaRouter.UNSELECT_REASON_STOPPED : 1198 MediaRouter.UNSELECT_REASON_DISCONNECTED); 1199 } 1200 dismiss(); 1201 } else if (id == R.id.mr_control_playback_ctrl) { 1202 if (mMediaController != null && mState != null) { 1203 boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_PLAYING; 1204 int actionDescResId = 0; 1205 if (isPlaying && isPauseActionSupported()) { 1206 mMediaController.getTransportControls().pause(); 1207 actionDescResId = R.string.mr_controller_pause; 1208 } else if (isPlaying && isStopActionSupported()) { 1209 mMediaController.getTransportControls().stop(); 1210 actionDescResId = R.string.mr_controller_stop; 1211 } else if (!isPlaying && isPlayActionSupported()){ 1212 mMediaController.getTransportControls().play(); 1213 actionDescResId = R.string.mr_controller_play; 1214 } 1215 // Announce the action for accessibility. 1216 if (mAccessibilityManager != null && mAccessibilityManager.isEnabled() 1217 && actionDescResId != 0) { 1218 AccessibilityEvent event = AccessibilityEvent.obtain( 1219 AccessibilityEventCompat.TYPE_ANNOUNCEMENT); 1220 event.setPackageName(mContext.getPackageName()); 1221 event.setClassName(getClass().getName()); 1222 event.getText().add(mContext.getString(actionDescResId)); 1223 mAccessibilityManager.sendAccessibilityEvent(event); 1224 } 1225 } 1226 } else if (id == R.id.mr_close) { 1227 dismiss(); 1228 } 1229 } 1230 } 1231 1232 private class VolumeChangeListener implements SeekBar.OnSeekBarChangeListener { 1233 private final Runnable mStopTrackingTouch = new Runnable() { 1234 @Override 1235 public void run() { 1236 if (mRouteInVolumeSliderTouched != null) { 1237 mRouteInVolumeSliderTouched = null; 1238 if (mHasPendingUpdate) { 1239 update(mPendingUpdateAnimationNeeded); 1240 } 1241 } 1242 } 1243 }; 1244 1245 VolumeChangeListener() { 1246 } 1247 1248 @Override 1249 public void onStartTrackingTouch(SeekBar seekBar) { 1250 if (mRouteInVolumeSliderTouched != null) { 1251 mVolumeSlider.removeCallbacks(mStopTrackingTouch); 1252 } 1253 mRouteInVolumeSliderTouched = (MediaRouter.RouteInfo) seekBar.getTag(); 1254 } 1255 1256 @Override 1257 public void onStopTrackingTouch(SeekBar seekBar) { 1258 // Defer resetting mVolumeSliderTouched to allow the media route provider 1259 // a little time to settle into its new state and publish the final 1260 // volume update. 1261 mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS); 1262 } 1263 1264 @Override 1265 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 1266 if (fromUser) { 1267 MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) seekBar.getTag(); 1268 if (DEBUG) { 1269 Log.d(TAG, "onProgressChanged(): calling " 1270 + "MediaRouter.RouteInfo.requestSetVolume(" + progress + ")"); 1271 } 1272 route.requestSetVolume(progress); 1273 } 1274 } 1275 } 1276 1277 private class VolumeGroupAdapter extends ArrayAdapter<MediaRouter.RouteInfo> { 1278 final float mDisabledAlpha; 1279 1280 public VolumeGroupAdapter(Context context, List<MediaRouter.RouteInfo> objects) { 1281 super(context, 0, objects); 1282 mDisabledAlpha = MediaRouterThemeHelper.getDisabledAlpha(context); 1283 } 1284 1285 @Override 1286 public boolean isEnabled(int position) { 1287 return false; 1288 } 1289 1290 @Override 1291 public View getView(final int position, View convertView, ViewGroup parent) { 1292 View v = convertView; 1293 if (v == null) { 1294 v = LayoutInflater.from(parent.getContext()).inflate( 1295 R.layout.mr_controller_volume_item, parent, false); 1296 } else { 1297 updateVolumeGroupItemHeight(v); 1298 } 1299 1300 MediaRouter.RouteInfo route = getItem(position); 1301 if (route != null) { 1302 boolean isEnabled = route.isEnabled(); 1303 1304 TextView routeName = (TextView) v.findViewById(R.id.mr_name); 1305 routeName.setEnabled(isEnabled); 1306 routeName.setText(route.getName()); 1307 1308 MediaRouteVolumeSlider volumeSlider = 1309 (MediaRouteVolumeSlider) v.findViewById(R.id.mr_volume_slider); 1310 MediaRouterThemeHelper.setVolumeSliderColor( 1311 parent.getContext(), volumeSlider, mVolumeGroupList); 1312 volumeSlider.setTag(route); 1313 mVolumeSliderMap.put(route, volumeSlider); 1314 volumeSlider.setHideThumb(!isEnabled); 1315 volumeSlider.setEnabled(isEnabled); 1316 if (isEnabled) { 1317 if (isVolumeControlAvailable(route)) { 1318 volumeSlider.setMax(route.getVolumeMax()); 1319 volumeSlider.setProgress(route.getVolume()); 1320 volumeSlider.setOnSeekBarChangeListener(mVolumeChangeListener); 1321 } else { 1322 volumeSlider.setMax(100); 1323 volumeSlider.setProgress(100); 1324 volumeSlider.setEnabled(false); 1325 } 1326 } 1327 1328 ImageView volumeItemIcon = 1329 (ImageView) v.findViewById(R.id.mr_volume_item_icon); 1330 volumeItemIcon.setAlpha(isEnabled ? 0xFF : (int) (0xFF * mDisabledAlpha)); 1331 1332 // If overlay bitmap exists, real view should remain hidden until 1333 // the animation ends. 1334 LinearLayout container = (LinearLayout) v.findViewById(R.id.volume_item_container); 1335 container.setVisibility(mGroupMemberRoutesAnimatingWithBitmap.contains(route) 1336 ? View.INVISIBLE : View.VISIBLE); 1337 1338 // Routes which are being added will be invisible until animation ends. 1339 if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { 1340 Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f); 1341 alphaAnim.setDuration(0); 1342 alphaAnim.setFillEnabled(true); 1343 alphaAnim.setFillAfter(true); 1344 v.clearAnimation(); 1345 v.startAnimation(alphaAnim); 1346 } 1347 } 1348 return v; 1349 } 1350 } 1351 1352 private class FetchArtTask extends AsyncTask<Void, Void, Bitmap> { 1353 // Show animation only when fetching takes a long time. 1354 private static final long SHOW_ANIM_TIME_THRESHOLD_MILLIS = 120L; 1355 1356 private final Bitmap mIconBitmap; 1357 private final Uri mIconUri; 1358 private int mBackgroundColor; 1359 private long mStartTimeMillis; 1360 1361 FetchArtTask() { 1362 Bitmap bitmap = mDescription == null ? null : mDescription.getIconBitmap(); 1363 if (isBitmapRecycled(bitmap)) { 1364 Log.w(TAG, "Can't fetch the given art bitmap because it's already recycled."); 1365 bitmap = null; 1366 } 1367 mIconBitmap = bitmap; 1368 mIconUri = mDescription == null ? null : mDescription.getIconUri(); 1369 } 1370 1371 public Bitmap getIconBitmap() { 1372 return mIconBitmap; 1373 } 1374 1375 public Uri getIconUri() { 1376 return mIconUri; 1377 } 1378 1379 @Override 1380 protected void onPreExecute() { 1381 mStartTimeMillis = SystemClock.uptimeMillis(); 1382 clearLoadedBitmap(); 1383 } 1384 1385 @Override 1386 protected Bitmap doInBackground(Void... arg) { 1387 Bitmap art = null; 1388 if (mIconBitmap != null) { 1389 art = mIconBitmap; 1390 } else if (mIconUri != null) { 1391 InputStream stream = null; 1392 try { 1393 if ((stream = openInputStreamByScheme(mIconUri)) == null) { 1394 Log.w(TAG, "Unable to open: " + mIconUri); 1395 return null; 1396 } 1397 // Query art size. 1398 BitmapFactory.Options options = new BitmapFactory.Options(); 1399 options.inJustDecodeBounds = true; 1400 BitmapFactory.decodeStream(stream, null, options); 1401 if (options.outWidth == 0 || options.outHeight == 0) { 1402 return null; 1403 } 1404 // Rewind the stream in order to restart art decoding. 1405 try { 1406 stream.reset(); 1407 } catch (IOException e) { 1408 // Failed to rewind the stream, try to reopen it. 1409 stream.close(); 1410 if ((stream = openInputStreamByScheme(mIconUri)) == null) { 1411 Log.w(TAG, "Unable to open: " + mIconUri); 1412 return null; 1413 } 1414 } 1415 // Calculate required size to decode the art and possibly resize it. 1416 options.inJustDecodeBounds = false; 1417 int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight); 1418 int ratio = options.outHeight / reqHeight; 1419 options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio)); 1420 if (isCancelled()) { 1421 return null; 1422 } 1423 art = BitmapFactory.decodeStream(stream, null, options); 1424 } catch (IOException e){ 1425 Log.w(TAG, "Unable to open: " + mIconUri, e); 1426 } finally { 1427 if (stream != null) { 1428 try { 1429 stream.close(); 1430 } catch (IOException e) { 1431 } 1432 } 1433 } 1434 } 1435 if (isBitmapRecycled(art)) { 1436 Log.w(TAG, "Can't use recycled bitmap: " + art); 1437 return null; 1438 } 1439 if (art != null && art.getWidth() < art.getHeight()) { 1440 // Portrait art requires dominant color as background color. 1441 Palette palette = new Palette.Builder(art).maximumColorCount(1).generate(); 1442 mBackgroundColor = palette.getSwatches().isEmpty() 1443 ? 0 : palette.getSwatches().get(0).getRgb(); 1444 } 1445 return art; 1446 } 1447 1448 @Override 1449 protected void onPostExecute(Bitmap art) { 1450 mFetchArtTask = null; 1451 if (!ObjectsCompat.equals(mArtIconBitmap, mIconBitmap) 1452 || !ObjectsCompat.equals(mArtIconUri, mIconUri)) { 1453 mArtIconBitmap = mIconBitmap; 1454 mArtIconLoadedBitmap = art; 1455 mArtIconUri = mIconUri; 1456 mArtIconBackgroundColor = mBackgroundColor; 1457 mArtIconIsLoaded = true; 1458 long elapsedTimeMillis = SystemClock.uptimeMillis() - mStartTimeMillis; 1459 // Loaded bitmap will be applied on the next update 1460 update(elapsedTimeMillis > SHOW_ANIM_TIME_THRESHOLD_MILLIS); 1461 } 1462 } 1463 1464 private InputStream openInputStreamByScheme(Uri uri) throws IOException { 1465 String scheme = uri.getScheme().toLowerCase(); 1466 InputStream stream = null; 1467 if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme) 1468 || ContentResolver.SCHEME_CONTENT.equals(scheme) 1469 || ContentResolver.SCHEME_FILE.equals(scheme)) { 1470 stream = mContext.getContentResolver().openInputStream(uri); 1471 } else { 1472 URL url = new URL(uri.toString()); 1473 URLConnection conn = url.openConnection(); 1474 conn.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS); 1475 conn.setReadTimeout(CONNECTION_TIMEOUT_MILLIS); 1476 stream = conn.getInputStream(); 1477 } 1478 return (stream == null) ? null : new BufferedInputStream(stream); 1479 } 1480 } 1481} 1482