MediaRouteControllerDialog.java revision 2b274bd3e06bd33c9b2a0851256afcda94d96395
1feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk/* 2feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Copyright (C) 2013 The Android Open Source Project 3feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 4feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Licensed under the Apache License, Version 2.0 (the "License"); 5feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * you may not use this file except in compliance with the License. 6feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * You may obtain a copy of the License at 7feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 8feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * http://www.apache.org/licenses/LICENSE-2.0 9feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 10feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Unless required by applicable law or agreed to in writing, software 11feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * distributed under the License is distributed on an "AS IS" BASIS, 12feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * See the License for the specific language governing permissions and 14feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * limitations under the License. 15feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 16feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 17feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkpackage android.support.v7.app; 185096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin 19feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport static android.widget.SeekBar.OnSeekBarChangeListener; 20feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 21feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.content.ContentResolver; 22feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.content.Context; 23feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.content.res.Configuration; 24feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.content.res.Resources; 25feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.content.res.TypedArray; 26feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.graphics.Bitmap; 27feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.graphics.BitmapFactory; 28feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.graphics.drawable.BitmapDrawable; 29feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.graphics.drawable.Drawable; 305096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkinimport android.net.Uri; 31feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.os.AsyncTask; 32feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.os.Build; 33feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.os.Bundle; 34feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.os.RemoteException; 35feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v4.media.MediaDescriptionCompat; 36feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v4.media.MediaMetadataCompat; 37ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunkimport android.support.v4.media.session.MediaControllerCompat; 38feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v4.media.session.MediaSessionCompat; 39feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v4.media.session.PlaybackStateCompat; 40feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v7.graphics.Palette; 4191b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunkimport android.support.v7.media.MediaRouteSelector; 4291b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunkimport android.support.v7.media.MediaRouter; 43feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v7.media.SeekBarJellybean; 44feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.support.v7.mediarouter.R; 45feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.text.TextUtils; 46feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.util.DisplayMetrics; 47feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.util.Log; 48feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.view.KeyEvent; 49feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.view.LayoutInflater; 50feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.view.View; 51feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.view.ViewGroup; 52feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.view.ViewTreeObserver; 53feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.ArrayAdapter; 54feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.Button; 55feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.FrameLayout; 56feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.ImageButton; 57feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.ImageView; 58feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.LinearLayout; 59feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.ListView; 60feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.RelativeLayout; 61feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.SeekBar; 62feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport android.widget.TextView; 63feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 64feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.io.BufferedInputStream; 65feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.io.IOException; 66feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkimport java.util.List; 67feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 68feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk/** 69feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * This class implements the route controller dialog for {@link MediaRouter}. 70feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * <p> 71feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * This dialog allows the user to control or disconnect from the currently selected route. 72feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * </p> 73feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 74feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * @see MediaRouteButton 75feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * @see MediaRouteActionProvider 76feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 77feb50af361e4305a25758966b6b5df2738c00259Ruben Brunkpublic class MediaRouteControllerDialog extends AlertDialog { 78feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final String TAG = "MediaRouteControllerDialog"; 79feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 80feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // STOPSHIP: Remove the flag when the group volume control implementation completes. 81feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final boolean USE_GROUP = false; 82feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 83feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Time to wait before updating the volume when the user lets go of the seek bar 84feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // to allow the route provider time to propagate the change and publish a new 85feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // route descriptor. 86feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private static final int VOLUME_UPDATE_DELAY_MILLIS = 250; 87feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 88feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private final MediaRouter mRouter; 89feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private final MediaRouterCallback mCallback; 90feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private final MediaRouter.RouteInfo mRoute; 91feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 92feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private boolean mCreated; 93feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private boolean mAttachedToWindow; 94feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int mOrientation; 95feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int mDialogWidthPortrait; 96feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int mDialogWidthLandscape; 97feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private int mDialogPaddingHorizontal; 98feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 99feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private View mCustomControlView; 100feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 101feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private Button mDisconnectButton; 102feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private Button mStopCastingButton; 103feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private ImageButton mPlayPauseButton; 104feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private ImageButton mCloseButton; 105feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private ImageButton mGroupExpandCollapseButton; 106feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 107feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private FrameLayout mCustomControlLayout; 108feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private FrameLayout mDefaultControlLayout; 109feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private boolean mNeedToAdjustControlFrameLayout = true; 110feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private ImageView mArtView; 111feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private TextView mTitleView; 112feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private TextView mSubtitleView; 113feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private TextView mRouteNameTextView; 114feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 115feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private boolean mVolumeControlEnabled = true; 116feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private LinearLayout mControlLayout; 117feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private RelativeLayout mPlaybackControl; 118feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private LinearLayout mVolumeControl; 119feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private View mDividerView; 120feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 121feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private ListView mVolumeGroupList; 122feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private SeekBar mVolumeSlider; 123feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private boolean mVolumeSliderTouched; 1245096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin 1255096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin private MediaControllerCompat mMediaController; 1265096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin private MediaControllerCallback mControllerCallback; 127feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private PlaybackStateCompat mState; 128feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private MediaDescriptionCompat mDescription; 129feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 130feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private FetchArtTask mFetchArtTask; 131feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private boolean mIsGroupExpanded; 132feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 133feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public MediaRouteControllerDialog(Context context) { 134feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk this(context, 0); 135feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 136feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 137feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public MediaRouteControllerDialog(Context context, int theme) { 138feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk super(MediaRouterThemeHelper.createThemedContext(context), theme); 139feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk context = getContext(); 140feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 141feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mControllerCallback = new MediaControllerCallback(); 142feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRouter = MediaRouter.getInstance(context); 143feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCallback = new MediaRouterCallback(); 144feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRoute = mRouter.getSelectedRoute(); 145feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk setMediaSession(mRouter.getMediaSessionToken()); 146feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 147feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 148feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 149feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Gets the route that this dialog is controlling. 150feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 151feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public MediaRouter.RouteInfo getRoute() { 152feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return mRoute; 153feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 154feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 1555096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin private MediaRouter.RouteGroup getGroup() { 156feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mRoute instanceof MediaRouter.RouteGroup) { 157feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return (MediaRouter.RouteGroup) mRoute; 158feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 1595096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin return null; 1605096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin } 1615096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin 1625096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin /** 1635096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * Provides the subclass an opportunity to create a view that will 1645096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * be included within the body of the dialog to offer additional media controls 1655096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * for the currently playing content. 1665096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * 1675096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * @param savedInstanceState The dialog's saved instance state. 1685096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * @return The media control view, or null if none. 1695096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin */ 1705096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin public View onCreateMediaControlView(Bundle savedInstanceState) { 1715096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin return null; 1725096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin } 1735096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin 1745096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin /** 1755096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}. 1765096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * 1775096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * @return The media control view, or null if none. 178feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 17991b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk public View getMediaControlView() { 18091b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk return mCustomControlView; 18191b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 18291b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk 18391b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk /** 184feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Sets whether to enable the volume slider and volume control using the volume keys 185feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * when the route supports it. 186feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * <p> 187feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * The default value is true. 1885096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin * </p> 1895096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin */ 190feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void setVolumeControlEnabled(boolean enable) { 191feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mVolumeControlEnabled != enable) { 192feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeControlEnabled = enable; 193feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mCreated) { 194feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk updateVolumeControl(); 195feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 196feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 197feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 198feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 199feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 200feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Returns whether to enable the volume slider and volume control using the volume keys 201feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * when the route supports it. 202feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 203feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public boolean isVolumeControlEnabled() { 204feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return mVolumeControlEnabled; 205feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 206feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 207feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 208feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * Set the session to use for metadata and transport controls. The dialog 209feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * will listen to changes on this session and update the UI automatically in 210feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * response to changes. 211feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * 212feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk * @param sessionToken The token for the session to use. 213feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk */ 214feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void setMediaSession(MediaSessionCompat.Token sessionToken) { 21591b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk if (mMediaController != null) { 21691b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk mMediaController.unregisterCallback(mControllerCallback); 21791b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk mMediaController = null; 21891b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 21991b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk if (sessionToken == null) { 22091b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk return; 22191b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 22291b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk if (!mAttachedToWindow) { 22391b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk return; 22491b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 22591b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk try { 22691b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk mMediaController = new MediaControllerCompat(getContext(), sessionToken); 22791b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } catch (RemoteException e) { 22891b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk Log.e(TAG, "Error creating media controller in setMediaSession.", e); 22991b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 23091b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk if (mMediaController != null) { 23191b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk mMediaController.registerCallback(mControllerCallback); 23291b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 23391b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk MediaMetadataCompat metadata = mMediaController == null ? null 23491b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk : mMediaController.getMetadata(); 23591b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk mDescription = metadata == null ? null : metadata.getDescription(); 23691b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk mState = mMediaController == null ? null : mMediaController.getPlaybackState(); 23791b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk update(); 23891b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 23991b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk 24091b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk /** 24191b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk * Gets the description being used by the default UI. 24291b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk * 24391b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk * @return The current description. 24491b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk */ 24591b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk public MediaSessionCompat.Token getMediaSession() { 24691b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk return mMediaController == null ? null : mMediaController.getSessionToken(); 24791b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk } 24891b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk 24991b9aabc9fa0c058ecc4a8b3f486540c28fe1cc0Ruben Brunk @Override 250feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk protected void onCreate(Bundle savedInstanceState) { 251feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk super.onCreate(savedInstanceState); 252feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 253feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk setContentView(R.layout.mr_controller_material_dialog_b); 254feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk View decorView = getWindow().getDecorView(); 255feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDialogPaddingHorizontal = decorView.getPaddingLeft() + decorView.getPaddingRight(); 256feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk Resources res = getContext().getResources(); 257feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDialogWidthPortrait = res.getDimensionPixelSize( 258feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk R.dimen.mr_dialog_content_width_portrait) + mDialogPaddingHorizontal; 259feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDialogWidthLandscape = res.getDimensionPixelSize( 260feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk R.dimen.mr_dialog_content_width_landscape) + mDialogPaddingHorizontal; 261feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 262feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk ClickListener listener = new ClickListener(); 263feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 264feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDisconnectButton = (Button) findViewById(R.id.mr_button_disconnect); 265feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDisconnectButton.setOnClickListener(listener); 266feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 267feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mStopCastingButton = (Button) findViewById(R.id.mr_button_stop); 268feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mStopCastingButton.setOnClickListener(listener); 269feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 270feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRouteNameTextView = (TextView) findViewById(R.id.mr_name); 271feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCloseButton = (ImageButton) findViewById(R.id.mr_close); 272feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCloseButton.setOnClickListener(listener); 273feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCustomControlLayout = (FrameLayout) findViewById(R.id.mr_custom_control); 274feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDefaultControlLayout = (FrameLayout) findViewById(R.id.mr_default_control); 275feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk ViewTreeObserver observer = mDefaultControlLayout.getViewTreeObserver(); 276feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 277feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 278feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onGlobalLayout() { 279feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk updateControlFrameLayout(); 280feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 281feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }); 282feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mArtView = (ImageView) findViewById(R.id.mr_art); 283feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 2845096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin mControlLayout = (LinearLayout) findViewById(R.id.mr_control); 2855096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin mDividerView = findViewById(R.id.mr_control_divider); 2865096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin 2875096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin mPlaybackControl = (RelativeLayout) findViewById(R.id.mr_playback_control); 288feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mTitleView = (TextView) findViewById(R.id.mr_control_title); 2895096defdaa4716ce81047a855d6e5ce3f8263600Igor Murashkin mSubtitleView = (TextView) findViewById(R.id.mr_control_subtitle); 290feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mPlayPauseButton = (ImageButton) findViewById(R.id.mr_control_play_pause); 291feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mPlayPauseButton.setOnClickListener(listener); 292feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 293feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeControl = (LinearLayout) findViewById(R.id.mr_volume_control); 294feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSlider = (SeekBar) findViewById(R.id.mr_volume_slider); 295feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 296feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private final Runnable mStopTrackingTouch = new Runnable() { 297feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 298feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void run() { 299feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mVolumeSliderTouched) { 300feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSliderTouched = false; 301feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk updateVolumeControl(); 302feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 303feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 304feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }; 305feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 306feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 307feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onStartTrackingTouch(SeekBar seekBar) { 308feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mVolumeSliderTouched) { 309feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSlider.removeCallbacks(mStopTrackingTouch); 310feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } else { 311feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSliderTouched = true; 312feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 313feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 314feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 315feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 316feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onStopTrackingTouch(SeekBar seekBar) { 317feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Defer resetting mVolumeSliderTouched to allow the media route provider 318feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // a little time to settle into its new state and publish the final 319feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // volume update. 320feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS); 321feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 322feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 323feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 324feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 325feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (fromUser) { 326feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRoute.requestSetVolume(progress); 327feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 328feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 329feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }); 330feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 331feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk TypedArray styledAttributes = getContext().obtainStyledAttributes(new int[] { 332feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk R.attr.mediaRouteExpandGroupDrawable, 333feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk R.attr.mediaRouteCollapseGroupDrawable 334feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }); 335feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk final Drawable expandGroupDrawable = styledAttributes.getDrawable(0); 336feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk final Drawable collapseGroupDrawable = styledAttributes.getDrawable(1); 337feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk styledAttributes.recycle(); 338feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 339feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeGroupList = (ListView)findViewById(R.id.mr_volume_group_list); 340ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mGroupExpandCollapseButton = (ImageButton) findViewById(R.id.mr_group_expand_collapse); 341feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() { 342feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 343feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onClick(View v) { 344feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mIsGroupExpanded = !mIsGroupExpanded; 345ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (mIsGroupExpanded) { 346ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mGroupExpandCollapseButton.setImageDrawable(collapseGroupDrawable); 347ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mVolumeGroupList.setVisibility(View.VISIBLE); 348feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeGroupList.setAdapter( 349feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk new VolumeGroupAdapter(getContext(), getGroup().getRoutes())); 350feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } else { 351feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mGroupExpandCollapseButton.setImageDrawable(expandGroupDrawable); 352ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mVolumeGroupList.setVisibility(View.GONE); 353feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 354feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mNeedToAdjustControlFrameLayout = true; 355feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 356feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk }); 357feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 358ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mCreated = true; 359feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCustomControlView = onCreateMediaControlView(savedInstanceState); 360feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mCustomControlView != null) { 361feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCustomControlLayout.addView(mCustomControlView); 362feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mCustomControlLayout.setVisibility(View.VISIBLE); 363ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mArtView.setVisibility(View.GONE); 364ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 365ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk update(); 366feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 367feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 368feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk /** 369ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk * Called by {@link MediaRouteControllerDialogFragment} when the device configuration 370ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk * is changed. 371ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk */ 372feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk void onConfigurationChanged(Configuration newConfig) { 373feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk onOrientationChanged(newConfig.orientation); 374ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 375feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 376feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void onOrientationChanged(int orientation) { 377ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (!mAttachedToWindow || mOrientation == orientation) { 378feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return; 379feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 380feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mOrientation = orientation; 381feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk int dialogWidth = mOrientation == Configuration.ORIENTATION_LANDSCAPE 382feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk ? mDialogWidthLandscape : mDialogWidthPortrait; 383ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk int buttonWidth = (dialogWidth - mDialogPaddingHorizontal) / 2; 384feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk getWindow().setLayout(dialogWidth, ViewGroup.LayoutParams.WRAP_CONTENT); 385feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 386feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk // Manually set the width of buttons to workaround unexpected text alignment changes. 387ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk ViewGroup.LayoutParams lp = mStopCastingButton.getLayoutParams(); 388ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk lp.width = buttonWidth; 389feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mStopCastingButton.setLayoutParams(lp); 390ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk 391feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk lp = mDisconnectButton.getLayoutParams(); 392feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk lp.width = buttonWidth; 393ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mDisconnectButton.setLayoutParams(lp); 394feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 395feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk updateArtView(); 396feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 397feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 398feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 399feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onAttachedToWindow() { 400ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk super.onAttachedToWindow(); 401feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mAttachedToWindow = true; 402feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 403feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback, 404feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS); 405ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk setMediaSession(mRouter.getMediaSessionToken()); 406feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk onOrientationChanged(getContext().getResources().getConfiguration().orientation); 407feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 408feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 409feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 410feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public void onDetachedFromWindow() { 411feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRouter.removeCallback(mCallback); 412feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk setMediaSession(null); 413ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mAttachedToWindow = false; 414feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk super.onDetachedFromWindow(); 415feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 416feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 417feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk @Override 418feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public boolean onKeyDown(int keyCode, KeyEvent event) { 419feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN 420feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 421ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1); 422ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk return true; 423feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 424ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk return super.onKeyDown(keyCode, event); 425feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 426feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 427ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk @Override 428feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk public boolean onKeyUp(int keyCode, KeyEvent event) { 429feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN 430feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk || keyCode == KeyEvent.KEYCODE_VOLUME_UP) { 431feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return true; 432feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 433ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk return super.onKeyUp(keyCode, event); 434feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 435feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 436feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void update() { 437ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (!mRoute.isSelected() || mRoute.isDefault()) { 438ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk dismiss(); 439feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return; 440ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 441feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (!mCreated) { 442feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk return; 443ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 444feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 445feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mRouteNameTextView.setText(mRoute.getName()); 446feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.INVISIBLE); 447feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 448feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mCustomControlView == null) { 449ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (mFetchArtTask != null) { 450feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mFetchArtTask.cancel(true); 451feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 452feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mFetchArtTask = new FetchArtTask(); 453ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mFetchArtTask.execute(); 454ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 455feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk updateVolumeControl(); 456ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk updatePlaybackControl(); 457feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 458feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 459feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void updateVolumeControl() { 460feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (!mVolumeSliderTouched) { 461feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (isVolumeControlAvailable()) { 462feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeControl.setVisibility(View.VISIBLE); 463feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSlider.setMax(mRoute.getVolumeMax()); 464feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mVolumeSlider.setProgress(mRoute.getVolume()); 465feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (USE_GROUP) { 466ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (getGroup() == null) { 467feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mGroupExpandCollapseButton.setVisibility(View.GONE); 468feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } else { 469ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mGroupExpandCollapseButton.setVisibility(View.VISIBLE); 470feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk VolumeGroupAdapter adapter = 471feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk (VolumeGroupAdapter) mVolumeGroupList.getAdapter(); 472ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk if (adapter != null) { 473feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk adapter.notifyDataSetChanged(); 474feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 475ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk } 476feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 477feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } else { 478ef14da32804b06bac872c9e0e14ce0e52120a0bdRuben Brunk mVolumeControl.setVisibility(View.GONE); 479feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 480feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk updateControlLayoutHeight(); 481feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 482feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk } 483feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 484feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk private void updatePlaybackControl() { 485feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk if (mCustomControlView == null && (mDescription != null || mState != null)) { 486feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk mPlaybackControl.setVisibility(View.VISIBLE); 487feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk CharSequence title = mDescription == null ? null : mDescription.getTitle(); 488feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk boolean hasTitle = !TextUtils.isEmpty(title); 489feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk 490feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk CharSequence subtitle = mDescription == null ? null : mDescription.getSubtitle(); 491feb50af361e4305a25758966b6b5df2738c00259Ruben Brunk boolean hasSubtitle = !TextUtils.isEmpty(subtitle); 492 493 if (!hasTitle && !hasSubtitle) { 494 if (mRoute.getPresentationDisplayId() 495 != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) { 496 // The user is currently casting screen. 497 mTitleView.setText(R.string.mr_controller_casting_screen); 498 } else { 499 mTitleView.setText((mState == null 500 || mState.getState() == PlaybackStateCompat.STATE_NONE) 501 ? R.string.mr_controller_no_media_selected 502 : R.string.mr_controller_no_info_available); 503 } 504 mTitleView.setEnabled(false); 505 mTitleView.setVisibility(View.VISIBLE); 506 mSubtitleView.setVisibility(View.GONE); 507 } else { 508 mTitleView.setText(title); 509 mTitleView.setEnabled(hasTitle); 510 mTitleView.setVisibility(hasTitle ? View.VISIBLE : View.GONE); 511 mSubtitleView.setText(subtitle); 512 mSubtitleView.setVisibility(hasSubtitle ? View.VISIBLE : View.GONE); 513 } 514 if (mState != null) { 515 boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING 516 || mState.getState() == PlaybackStateCompat.STATE_PLAYING; 517 boolean supportsPlay = (mState.getActions() & (PlaybackStateCompat.ACTION_PLAY 518 | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0; 519 boolean supportsPause = (mState.getActions() & (PlaybackStateCompat.ACTION_PAUSE 520 | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0; 521 if (isPlaying && supportsPause) { 522 mPlayPauseButton.setVisibility(View.VISIBLE); 523 mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource( 524 getContext(), R.attr.mediaRoutePauseDrawable)); 525 mPlayPauseButton.setContentDescription(getContext().getResources() 526 .getText(R.string.mr_controller_pause)); 527 } else if (!isPlaying && supportsPlay) { 528 mPlayPauseButton.setVisibility(View.VISIBLE); 529 mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource( 530 getContext(), R.attr.mediaRoutePlayDrawable)); 531 mPlayPauseButton.setContentDescription(getContext().getResources() 532 .getText(R.string.mr_controller_play)); 533 } else { 534 mPlayPauseButton.setVisibility(View.GONE); 535 } 536 } 537 } else { 538 mPlaybackControl.setVisibility(View.GONE); 539 } 540 updateControlLayoutHeight(); 541 } 542 543 private void updateControlLayoutHeight() { 544 // TODO: Update the top and bottom padding of the control layout according to the display 545 // height. 546 mDividerView.setVisibility((mVolumeControl.getVisibility() == View.VISIBLE 547 && mPlaybackControl.getVisibility() == View.VISIBLE) 548 ? View.VISIBLE : View.GONE); 549 mControlLayout.setVisibility((mVolumeControl.getVisibility() == View.GONE 550 && mPlaybackControl.getVisibility() == View.GONE) 551 ? View.GONE : View.VISIBLE); 552 } 553 554 private void updateControlFrameLayout() { 555 if (!mNeedToAdjustControlFrameLayout) { 556 return; 557 } 558 int height; 559 if (mArtView.getVisibility() == View.GONE) { 560 height = LinearLayout.LayoutParams.WRAP_CONTENT; 561 } else if (!mIsGroupExpanded) { 562 height = mArtView.getHeight() + mControlLayout.getHeight(); 563 } else { 564 if (mVolumeGroupList.getAdapter().getCount() <= 2) { 565 // Push the controls up and cover the artwork. 566 height = mArtView.getHeight() + mControlLayout.getHeight() 567 - mVolumeGroupList.getHeight(); 568 } else { 569 // If there are 3 or more, the controls completely cover the artwork. 570 height = mControlLayout.getHeight(); 571 } 572 } 573 mDefaultControlLayout.setLayoutParams(new LinearLayout.LayoutParams( 574 LinearLayout.LayoutParams.FILL_PARENT, height)); 575 mNeedToAdjustControlFrameLayout = false; 576 } 577 578 private boolean isVolumeControlAvailable() { 579 return mVolumeControlEnabled && mRoute.getVolumeHandling() == 580 MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE; 581 } 582 583 private void updateArtView() { 584 mNeedToAdjustControlFrameLayout = true; 585 if (!(mArtView.getDrawable() instanceof BitmapDrawable)) { 586 mArtView.setVisibility(View.GONE); 587 return; 588 } 589 Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap(); 590 if (art == null) { 591 mArtView.setVisibility(View.GONE); 592 return; 593 } 594 int desiredArtHeight = getDesiredArtHeight(art.getWidth(), art.getHeight()); 595 DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); 596 int dialogWidth = displayMetrics.widthPixels < displayMetrics.heightPixels 597 ? mDialogWidthPortrait : mDialogWidthLandscape; 598 View decorView = getWindow().getDecorView(); 599 decorView.measure(dialogWidth, View.MeasureSpec.UNSPECIFIED); 600 // Show art if and only if it fits in the screen. 601 if (mArtView.getVisibility() == View.GONE) { 602 if (decorView.getMeasuredHeight() + desiredArtHeight <= displayMetrics.heightPixels) { 603 mArtView.setVisibility(View.VISIBLE); 604 mArtView.setMaxHeight(desiredArtHeight); 605 } 606 } else { 607 if (decorView.getMeasuredHeight() - mArtView.getMeasuredHeight() + desiredArtHeight 608 <= displayMetrics.heightPixels) { 609 mArtView.setMaxHeight(desiredArtHeight); 610 } else { 611 mArtView.setVisibility(View.GONE); 612 } 613 } 614 } 615 616 /** 617 * Returns desired art height to fit into controller dialog. 618 */ 619 private int getDesiredArtHeight(int originalWidth, int originalHeight) { 620 int dialogWidth = getWindow().getAttributes().width - mDialogPaddingHorizontal; 621 if (originalWidth >= originalHeight) { 622 // For landscape art, fit width to dialog width. 623 return (int) ((float) dialogWidth * originalHeight / originalWidth + 0.5f); 624 } 625 // For portrait art, fit height to 16:9 ratio case's height. 626 return (int) ((float) dialogWidth * 9 / 16 + 0.5f); 627 } 628 629 private final class MediaRouterCallback extends MediaRouter.Callback { 630 @Override 631 public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) { 632 update(); 633 } 634 635 @Override 636 public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) { 637 update(); 638 } 639 640 @Override 641 public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) { 642 if (route == mRoute) { 643 updateVolumeControl(); 644 } 645 } 646 } 647 648 private final class MediaControllerCallback extends MediaControllerCompat.Callback { 649 @Override 650 public void onSessionDestroyed() { 651 if (mMediaController != null) { 652 mMediaController.unregisterCallback(mControllerCallback); 653 mMediaController = null; 654 } 655 } 656 657 @Override 658 public void onPlaybackStateChanged(PlaybackStateCompat state) { 659 mState = state; 660 update(); 661 } 662 663 @Override 664 public void onMetadataChanged(MediaMetadataCompat metadata) { 665 mDescription = metadata == null ? null : metadata.getDescription(); 666 update(); 667 } 668 } 669 670 private final class ClickListener implements View.OnClickListener { 671 @Override 672 public void onClick(View v) { 673 int id = v.getId(); 674 if (id == R.id.mr_button_stop || id == R.id.mr_button_disconnect) { 675 if (mRoute.isSelected()) { 676 mRouter.unselect(id == R.id.mr_button_stop ? 677 MediaRouter.UNSELECT_REASON_STOPPED : 678 MediaRouter.UNSELECT_REASON_DISCONNECTED); 679 } 680 dismiss(); 681 } else if (id == R.id.mr_control_play_pause) { 682 if (mMediaController != null && mState != null) { 683 if (mState.getState() == PlaybackStateCompat.STATE_PLAYING) { 684 mMediaController.getTransportControls().pause(); 685 } else { 686 mMediaController.getTransportControls().play(); 687 } 688 } 689 } else if (id == R.id.mr_close) { 690 dismiss(); 691 } 692 } 693 } 694 695 private class VolumeGroupAdapter extends ArrayAdapter<MediaRouter.RouteInfo> { 696 final static float DISABLED_ALPHA = .3f; 697 698 final OnSeekBarChangeListener mOnSeekBarChangeListener = new OnSeekBarChangeListener() { 699 @Override 700 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 701 if (fromUser) { 702 int position = (int) seekBar.getTag(); 703 getGroup().getRouteAt(position).requestSetVolume(progress); 704 } 705 } 706 707 @Override 708 public void onStartTrackingTouch(SeekBar seekBar) { 709 // TODO: Implement 710 } 711 712 @Override 713 public void onStopTrackingTouch(SeekBar seekBar) { 714 // TODO: Implement 715 } 716 }; 717 718 public VolumeGroupAdapter(Context context, List<MediaRouter.RouteInfo> objects) { 719 super(context, 0, objects); 720 } 721 722 @Override 723 public View getView(final int position, View convertView, ViewGroup parent) { 724 View v = convertView; 725 if (v == null) { 726 v = LayoutInflater.from(getContext()).inflate( 727 R.layout.mr_controller_volume_item, null); 728 } 729 MediaRouter.RouteInfo route = getItem(position); 730 if (route != null) { 731 boolean isEnabled = route.isEnabled(); 732 733 TextView routeName = (TextView) v.findViewById(R.id.mr_name); 734 routeName.setEnabled(isEnabled); 735 routeName.setText(route.getName()); 736 737 SeekBar volumeSlider = (SeekBar) v.findViewById(R.id.mr_volume_slider); 738 volumeSlider.setEnabled(isEnabled); 739 volumeSlider.setTag(position); 740 if (isEnabled) { 741 if (route.getVolumeHandling() 742 == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) { 743 volumeSlider.setMax(route.getVolumeMax()); 744 volumeSlider.setProgress(route.getVolume()); 745 volumeSlider.setOnSeekBarChangeListener(mOnSeekBarChangeListener); 746 } else { 747 volumeSlider.setMax(100); 748 volumeSlider.setProgress(100); 749 volumeSlider.setEnabled(false); 750 } 751 } 752 if (Build.VERSION.SDK_INT >= 16) { 753 SeekBarJellybean.getThumb(volumeSlider).mutate().setAlpha(isEnabled ? 255 : 0); 754 // TODO: Still see an artifact even though the thumb is transparent. Remove it. 755 } 756 757 ImageView volumeItemIcon = 758 (ImageView) v.findViewById(R.id.mr_volume_item_icon); 759 volumeItemIcon.setAlpha(isEnabled ? 255 : (int) (255 * DISABLED_ALPHA)); 760 } 761 return v; 762 } 763 } 764 765 private class FetchArtTask extends AsyncTask<Void, Void, Bitmap> { 766 private int mBackgroundColor; 767 768 @Override 769 protected Bitmap doInBackground(Void... arg) { 770 Bitmap art = null; 771 if (mDescription == null) { 772 return null; 773 } 774 if (mDescription.getIconBitmap() != null) { 775 art = mDescription.getIconBitmap(); 776 } else if (mDescription.getIconUri() != null) { 777 Uri iconUri = mDescription.getIconUri(); 778 String scheme = iconUri.getScheme(); 779 if (!(ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme) 780 || ContentResolver.SCHEME_CONTENT.equals(scheme) 781 || ContentResolver.SCHEME_FILE.equals(scheme))) { 782 Log.w(TAG, "Icon Uri should point to local resources."); 783 return null; 784 } 785 BufferedInputStream stream = null; 786 try { 787 stream = new BufferedInputStream( 788 getContext().getContentResolver().openInputStream(iconUri)); 789 790 // Query art size. 791 BitmapFactory.Options options = new BitmapFactory.Options(); 792 options.inJustDecodeBounds = true; 793 BitmapFactory.decodeStream(stream, null, options); 794 if (options.outWidth == 0 || options.outHeight == 0) { 795 return null; 796 } 797 // Rewind the stream in order to restart art decoding. 798 try { 799 stream.reset(); 800 } catch (IOException e) { 801 // Failed to rewind the stream, try to reopen it. 802 stream.close(); 803 stream = new BufferedInputStream(getContext().getContentResolver() 804 .openInputStream(iconUri)); 805 } 806 // Calculate required size to decode the art and possibly resize it. 807 options.inJustDecodeBounds = false; 808 int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight); 809 int ratio = options.outHeight / reqHeight; 810 options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio)); 811 if (isCancelled()) { 812 return null; 813 } 814 art = BitmapFactory.decodeStream(stream, null, options); 815 } catch (IOException e){ 816 Log.w(TAG, "Unable to open content: " + iconUri, e); 817 } finally { 818 if (stream != null) { 819 try { 820 stream.close(); 821 } catch (IOException e) { 822 } 823 } 824 } 825 } 826 if (art != null && art.getWidth() < art.getHeight()) { 827 // Portrait art requires background color. 828 Palette palette = new Palette.Builder(art).maximumColorCount(1).generate(); 829 mBackgroundColor = (palette.getSwatches() == null) 830 ? 0 : palette.getSwatches().get(0).getRgb(); 831 } 832 return art; 833 } 834 835 @Override 836 protected void onCancelled() { 837 mFetchArtTask = null; 838 } 839 840 @Override 841 protected void onPostExecute(Bitmap art) { 842 mFetchArtTask = null; 843 mArtView.setImageBitmap(art); 844 mArtView.setBackgroundColor(mBackgroundColor); 845 updateArtView(); 846 } 847 } 848} 849