MediaRouteControllerDialog.java revision f4575634e9f3c2b61c2e6bceaeed6cabb378803f
111417b1cfde8f1749905f2d735623af9214148afJeff Brown/*
211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Copyright (C) 2013 The Android Open Source Project
311417b1cfde8f1749905f2d735623af9214148afJeff Brown *
411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
511417b1cfde8f1749905f2d735623af9214148afJeff Brown * you may not use this file except in compliance with the License.
611417b1cfde8f1749905f2d735623af9214148afJeff Brown * You may obtain a copy of the License at
711417b1cfde8f1749905f2d735623af9214148afJeff Brown *
811417b1cfde8f1749905f2d735623af9214148afJeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
911417b1cfde8f1749905f2d735623af9214148afJeff Brown *
1011417b1cfde8f1749905f2d735623af9214148afJeff Brown * Unless required by applicable law or agreed to in writing, software
1111417b1cfde8f1749905f2d735623af9214148afJeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
1211417b1cfde8f1749905f2d735623af9214148afJeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1311417b1cfde8f1749905f2d735623af9214148afJeff Brown * See the License for the specific language governing permissions and
1411417b1cfde8f1749905f2d735623af9214148afJeff Brown * limitations under the License.
1511417b1cfde8f1749905f2d735623af9214148afJeff Brown */
1611417b1cfde8f1749905f2d735623af9214148afJeff Brown
1711417b1cfde8f1749905f2d735623af9214148afJeff Brownpackage android.support.v7.app;
1811417b1cfde8f1749905f2d735623af9214148afJeff Brown
19bfb90b6717bb46535d747aa50045a206965542d0Jae Seoimport static android.widget.SeekBar.OnSeekBarChangeListener;
20bfb90b6717bb46535d747aa50045a206965542d0Jae Seo
21005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport android.content.ContentResolver;
2211417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.content.Context;
236051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seoimport android.content.res.TypedArray;
24005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport android.graphics.Bitmap;
25005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport android.graphics.BitmapFactory;
2686a802c8d09983d8725cccf59217e65d3e0c6373Jae Seoimport android.graphics.PorterDuff;
2713d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kimimport android.graphics.drawable.BitmapDrawable;
282ef36d857302c5cd738c7c8bdec53d31feebebbaJeff Brownimport android.graphics.drawable.Drawable;
29005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport android.net.Uri;
30005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport android.os.AsyncTask;
31418c688351713e9349227262163eef752a8e7ce6Jae Seoimport android.os.Build;
3211417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.os.Bundle;
3394be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.os.RemoteException;
3494be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.support.v4.media.MediaDescriptionCompat;
3594be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.support.v4.media.MediaMetadataCompat;
3694be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.support.v4.media.session.MediaControllerCompat;
3794be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.support.v4.media.session.MediaSessionCompat;
3894be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.support.v4.media.session.PlaybackStateCompat;
39005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport android.support.v7.graphics.Palette;
4011417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.support.v7.media.MediaRouteSelector;
4111417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.support.v7.media.MediaRouter;
42418c688351713e9349227262163eef752a8e7ce6Jae Seoimport android.support.v7.media.SeekBarJellybean;
4311417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.support.v7.mediarouter.R;
4494be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.text.TextUtils;
4513d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kimimport android.util.DisplayMetrics;
4694be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.util.Log;
4706991009e52024cbb3dde550988e06149e77a269Jae Seoimport android.util.TypedValue;
4811417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.view.KeyEvent;
496051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seoimport android.view.LayoutInflater;
5011417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.view.View;
5140dd800ece983a94ea6a26a27481da50f489e7c8Jaewan Kimimport android.view.View.MeasureSpec;
526051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seoimport android.view.ViewGroup;
536051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seoimport android.widget.ArrayAdapter;
5411417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.widget.Button;
5511417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.widget.FrameLayout;
5694be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.widget.ImageButton;
5794be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.widget.ImageView;
58f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shahimport android.widget.LinearLayout;
596051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seoimport android.widget.ListView;
607ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Limimport android.widget.RelativeLayout;
61f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shahimport android.widget.SeekBar;
6294be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.widget.TextView;
6311417b1cfde8f1749905f2d735623af9214148afJeff Brown
64005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport java.io.BufferedInputStream;
65005482649154d0970693b3967241eb4d2cb22003Jaewan Kimimport java.io.IOException;
666051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seoimport java.util.List;
67005482649154d0970693b3967241eb4d2cb22003Jaewan Kim
6811417b1cfde8f1749905f2d735623af9214148afJeff Brown/**
6911417b1cfde8f1749905f2d735623af9214148afJeff Brown * This class implements the route controller dialog for {@link MediaRouter}.
7011417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p>
7111417b1cfde8f1749905f2d735623af9214148afJeff Brown * This dialog allows the user to control or disconnect from the currently selected route.
7211417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p>
7311417b1cfde8f1749905f2d735623af9214148afJeff Brown *
7411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteButton
7511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteActionProvider
7611417b1cfde8f1749905f2d735623af9214148afJeff Brown */
7789530d511efd566d53bedd9178454dd01d227c61Jae Seopublic class MediaRouteControllerDialog extends AlertDialog {
7811417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final String TAG = "MediaRouteControllerDialog";
7911417b1cfde8f1749905f2d735623af9214148afJeff Brown
80a4bcfe765789ebef71c36cbb2dba3f9f881fa5b4Jae Seo    // STOPSHIP: Remove the flag when the group volume control implementation completes.
81a4bcfe765789ebef71c36cbb2dba3f9f881fa5b4Jae Seo    private static final boolean USE_GROUP = false;
82a4bcfe765789ebef71c36cbb2dba3f9f881fa5b4Jae Seo
83f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    // Time to wait before updating the volume when the user lets go of the seek bar
84f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    // to allow the route provider time to propagate the change and publish a new
85f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    // route descriptor.
86f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    private static final int VOLUME_UPDATE_DELAY_MILLIS = 250;
87f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah
8806991009e52024cbb3dde550988e06149e77a269Jae Seo    private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
8906991009e52024cbb3dde550988e06149e77a269Jae Seo    private static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
9006991009e52024cbb3dde550988e06149e77a269Jae Seo    private static final int BUTTON_STOP_RES_ID = android.R.id.button1;
9106991009e52024cbb3dde550988e06149e77a269Jae Seo
9211417b1cfde8f1749905f2d735623af9214148afJeff Brown    private final MediaRouter mRouter;
9311417b1cfde8f1749905f2d735623af9214148afJeff Brown    private final MediaRouterCallback mCallback;
9411417b1cfde8f1749905f2d735623af9214148afJeff Brown    private final MediaRouter.RouteInfo mRoute;
9511417b1cfde8f1749905f2d735623af9214148afJeff Brown
96eff7719415542ba819054863b0995f07742a7a8aJeff Brown    private boolean mCreated;
97e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik    private boolean mAttachedToWindow;
98e2148398ae5d03c22789aebc3d6359b5ff86c077Jae Seo
99f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo    private int mDialogContentWidth;
10011417b1cfde8f1749905f2d735623af9214148afJeff Brown
1017ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim    private View mCustomControlView;
10211417b1cfde8f1749905f2d735623af9214148afJeff Brown
10311417b1cfde8f1749905f2d735623af9214148afJeff Brown    private Button mDisconnectButton;
10494be6100218126ce6a08bf1f56209578500b361fRoboErik    private Button mStopCastingButton;
1051ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private ImageButton mPlayPauseButton;
106b48ccf48b0281e69262fc643d0def898aede407bSungsoo Lim    private ImageButton mCloseButton;
1071ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private ImageButton mGroupExpandCollapseButton;
10894be6100218126ce6a08bf1f56209578500b361fRoboErik
1091ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private FrameLayout mCustomControlLayout;
1102b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang    private FrameLayout mDefaultControlLayout;
11194be6100218126ce6a08bf1f56209578500b361fRoboErik    private ImageView mArtView;
1121ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private TextView mTitleView;
1131ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private TextView mSubtitleView;
1147ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim    private TextView mRouteNameTextView;
11594be6100218126ce6a08bf1f56209578500b361fRoboErik
116f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    private boolean mVolumeControlEnabled = true;
1178e1281a26eaabd6422fd84c11010134a0a9c3338Jaewan Kim    private LinearLayout mMediaControlLayout;
1181ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private RelativeLayout mPlaybackControl;
1191ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private LinearLayout mVolumeControl;
1201ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private View mDividerView;
1211ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
1226051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo    private ListView mVolumeGroupList;
1231ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private SeekBar mVolumeSlider;
1241ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private boolean mVolumeSliderTouched;
12586a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo    private int mVolumeSliderColor;
126f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah
12794be6100218126ce6a08bf1f56209578500b361fRoboErik    private MediaControllerCompat mMediaController;
12894be6100218126ce6a08bf1f56209578500b361fRoboErik    private MediaControllerCallback mControllerCallback;
12994be6100218126ce6a08bf1f56209578500b361fRoboErik    private PlaybackStateCompat mState;
13094be6100218126ce6a08bf1f56209578500b361fRoboErik    private MediaDescriptionCompat mDescription;
13194be6100218126ce6a08bf1f56209578500b361fRoboErik
132005482649154d0970693b3967241eb4d2cb22003Jaewan Kim    private FetchArtTask mFetchArtTask;
1338bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo    private Bitmap mArtIconBitmap;
1348bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo    private Uri mArtIconUri;
1352b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang    private boolean mIsGroupExpanded;
13611417b1cfde8f1749905f2d735623af9214148afJeff Brown
13711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public MediaRouteControllerDialog(Context context) {
13811417b1cfde8f1749905f2d735623af9214148afJeff Brown        this(context, 0);
13911417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
14011417b1cfde8f1749905f2d735623af9214148afJeff Brown
14111417b1cfde8f1749905f2d735623af9214148afJeff Brown    public MediaRouteControllerDialog(Context context, int theme) {
14294be6100218126ce6a08bf1f56209578500b361fRoboErik        super(MediaRouterThemeHelper.createThemedContext(context), theme);
14311417b1cfde8f1749905f2d735623af9214148afJeff Brown        context = getContext();
14411417b1cfde8f1749905f2d735623af9214148afJeff Brown
14594be6100218126ce6a08bf1f56209578500b361fRoboErik        mControllerCallback = new MediaControllerCallback();
14611417b1cfde8f1749905f2d735623af9214148afJeff Brown        mRouter = MediaRouter.getInstance(context);
14711417b1cfde8f1749905f2d735623af9214148afJeff Brown        mCallback = new MediaRouterCallback();
14811417b1cfde8f1749905f2d735623af9214148afJeff Brown        mRoute = mRouter.getSelectedRoute();
149e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        setMediaSession(mRouter.getMediaSessionToken());
15011417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
15111417b1cfde8f1749905f2d735623af9214148afJeff Brown
15211417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
15311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Gets the route that this dialog is controlling.
15411417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
15511417b1cfde8f1749905f2d735623af9214148afJeff Brown    public MediaRouter.RouteInfo getRoute() {
15611417b1cfde8f1749905f2d735623af9214148afJeff Brown        return mRoute;
15711417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
15811417b1cfde8f1749905f2d735623af9214148afJeff Brown
1596051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo    private MediaRouter.RouteGroup getGroup() {
1606051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        if (mRoute instanceof MediaRouter.RouteGroup) {
1616051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            return (MediaRouter.RouteGroup) mRoute;
1626051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        }
1636051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        return null;
1646051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo    }
1656051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
16611417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
16711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Provides the subclass an opportunity to create a view that will
16811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * be included within the body of the dialog to offer additional media controls
16911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * for the currently playing content.
17011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
17111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param savedInstanceState The dialog's saved instance state.
17211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return The media control view, or null if none.
17311417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
17411417b1cfde8f1749905f2d735623af9214148afJeff Brown    public View onCreateMediaControlView(Bundle savedInstanceState) {
17511417b1cfde8f1749905f2d735623af9214148afJeff Brown        return null;
17611417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
17711417b1cfde8f1749905f2d735623af9214148afJeff Brown
17811417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
17911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Gets the media control view that was created by {@link #onCreateMediaControlView(Bundle)}.
18011417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
18111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return The media control view, or null if none.
18211417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
18311417b1cfde8f1749905f2d735623af9214148afJeff Brown    public View getMediaControlView() {
1847ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        return mCustomControlView;
18511417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
18611417b1cfde8f1749905f2d735623af9214148afJeff Brown
187eff7719415542ba819054863b0995f07742a7a8aJeff Brown    /**
188f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * Sets whether to enable the volume slider and volume control using the volume keys
189f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * when the route supports it.
190f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * <p>
191f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * The default value is true.
192f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * </p>
193f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     */
194f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    public void setVolumeControlEnabled(boolean enable) {
195f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah        if (mVolumeControlEnabled != enable) {
196f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah            mVolumeControlEnabled = enable;
197f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah            if (mCreated) {
1981ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                updateVolumeControl();
199f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah            }
200f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah        }
201f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    }
202f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah
203f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    /**
204f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * Returns whether to enable the volume slider and volume control using the volume keys
205f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     * when the route supports it.
206f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah     */
207f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    public boolean isVolumeControlEnabled() {
208f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah        return mVolumeControlEnabled;
209f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    }
210f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah
211f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    /**
21294be6100218126ce6a08bf1f56209578500b361fRoboErik     * Set the session to use for metadata and transport controls. The dialog
21394be6100218126ce6a08bf1f56209578500b361fRoboErik     * will listen to changes on this session and update the UI automatically in
21494be6100218126ce6a08bf1f56209578500b361fRoboErik     * response to changes.
21594be6100218126ce6a08bf1f56209578500b361fRoboErik     *
21694be6100218126ce6a08bf1f56209578500b361fRoboErik     * @param sessionToken The token for the session to use.
217eff7719415542ba819054863b0995f07742a7a8aJeff Brown     */
218e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik    private void setMediaSession(MediaSessionCompat.Token sessionToken) {
21994be6100218126ce6a08bf1f56209578500b361fRoboErik        if (mMediaController != null) {
22094be6100218126ce6a08bf1f56209578500b361fRoboErik            mMediaController.unregisterCallback(mControllerCallback);
22194be6100218126ce6a08bf1f56209578500b361fRoboErik            mMediaController = null;
22294be6100218126ce6a08bf1f56209578500b361fRoboErik        }
22394be6100218126ce6a08bf1f56209578500b361fRoboErik        if (sessionToken == null) {
22494be6100218126ce6a08bf1f56209578500b361fRoboErik            return;
22594be6100218126ce6a08bf1f56209578500b361fRoboErik        }
226e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        if (!mAttachedToWindow) {
227e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik            return;
228e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        }
22994be6100218126ce6a08bf1f56209578500b361fRoboErik        try {
23094be6100218126ce6a08bf1f56209578500b361fRoboErik            mMediaController = new MediaControllerCompat(getContext(), sessionToken);
23194be6100218126ce6a08bf1f56209578500b361fRoboErik        } catch (RemoteException e) {
23294be6100218126ce6a08bf1f56209578500b361fRoboErik            Log.e(TAG, "Error creating media controller in setMediaSession.", e);
23394be6100218126ce6a08bf1f56209578500b361fRoboErik        }
23494be6100218126ce6a08bf1f56209578500b361fRoboErik        if (mMediaController != null) {
23594be6100218126ce6a08bf1f56209578500b361fRoboErik            mMediaController.registerCallback(mControllerCallback);
236eff7719415542ba819054863b0995f07742a7a8aJeff Brown        }
23794be6100218126ce6a08bf1f56209578500b361fRoboErik        MediaMetadataCompat metadata = mMediaController == null ? null
23894be6100218126ce6a08bf1f56209578500b361fRoboErik                : mMediaController.getMetadata();
23994be6100218126ce6a08bf1f56209578500b361fRoboErik        mDescription = metadata == null ? null : metadata.getDescription();
24094be6100218126ce6a08bf1f56209578500b361fRoboErik        mState = mMediaController == null ? null : mMediaController.getPlaybackState();
24194be6100218126ce6a08bf1f56209578500b361fRoboErik        update();
242eff7719415542ba819054863b0995f07742a7a8aJeff Brown    }
243eff7719415542ba819054863b0995f07742a7a8aJeff Brown
244eff7719415542ba819054863b0995f07742a7a8aJeff Brown    /**
24594be6100218126ce6a08bf1f56209578500b361fRoboErik     * Gets the description being used by the default UI.
24694be6100218126ce6a08bf1f56209578500b361fRoboErik     *
24794be6100218126ce6a08bf1f56209578500b361fRoboErik     * @return The current description.
248eff7719415542ba819054863b0995f07742a7a8aJeff Brown     */
24994be6100218126ce6a08bf1f56209578500b361fRoboErik    public MediaSessionCompat.Token getMediaSession() {
25094be6100218126ce6a08bf1f56209578500b361fRoboErik        return mMediaController == null ? null : mMediaController.getSessionToken();
251eff7719415542ba819054863b0995f07742a7a8aJeff Brown    }
252eff7719415542ba819054863b0995f07742a7a8aJeff Brown
25311417b1cfde8f1749905f2d735623af9214148afJeff Brown    @Override
25411417b1cfde8f1749905f2d735623af9214148afJeff Brown    protected void onCreate(Bundle savedInstanceState) {
25511417b1cfde8f1749905f2d735623af9214148afJeff Brown        super.onCreate(savedInstanceState);
25611417b1cfde8f1749905f2d735623af9214148afJeff Brown
257a09c7b30d3a5c53281db955eabce171e1aec6feaSungsoo Lim        setContentView(R.layout.mr_controller_material_dialog_b);
258f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo
259f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        int dialogWidth = MediaRouteDialogHelper.getDialogWidth(getContext());
260f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        getWindow().setLayout(dialogWidth, ViewGroup.LayoutParams.WRAP_CONTENT);
261f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo
262f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        View decorView = getWindow().getDecorView();
263f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        mDialogContentWidth =
264f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo                dialogWidth - decorView.getPaddingLeft() - decorView.getPaddingRight();
26511417b1cfde8f1749905f2d735623af9214148afJeff Brown
26606991009e52024cbb3dde550988e06149e77a269Jae Seo        // Remove the neutral button.
26706991009e52024cbb3dde550988e06149e77a269Jae Seo        findViewById(BUTTON_NEUTRAL_RES_ID).setVisibility(View.GONE);
26806991009e52024cbb3dde550988e06149e77a269Jae Seo
26994be6100218126ce6a08bf1f56209578500b361fRoboErik        ClickListener listener = new ClickListener();
270d6748a7926ebb2450337f9f0ff4d445c980344e5Jeff Brown
27106991009e52024cbb3dde550988e06149e77a269Jae Seo        mDisconnectButton = (Button) findViewById(BUTTON_DISCONNECT_RES_ID);
27206991009e52024cbb3dde550988e06149e77a269Jae Seo        mDisconnectButton.setText(R.string.mr_controller_disconnect);
27394be6100218126ce6a08bf1f56209578500b361fRoboErik        mDisconnectButton.setOnClickListener(listener);
27411417b1cfde8f1749905f2d735623af9214148afJeff Brown
27506991009e52024cbb3dde550988e06149e77a269Jae Seo        mStopCastingButton = (Button) findViewById(BUTTON_STOP_RES_ID);
27606991009e52024cbb3dde550988e06149e77a269Jae Seo        mStopCastingButton.setText(R.string.mr_controller_stop);
27794be6100218126ce6a08bf1f56209578500b361fRoboErik        mStopCastingButton.setOnClickListener(listener);
27811417b1cfde8f1749905f2d735623af9214148afJeff Brown
27906991009e52024cbb3dde550988e06149e77a269Jae Seo        TypedValue value = new TypedValue();
28006991009e52024cbb3dde550988e06149e77a269Jae Seo        if (getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true)) {
28106991009e52024cbb3dde550988e06149e77a269Jae Seo            mDisconnectButton.setTextColor(value.data);
28206991009e52024cbb3dde550988e06149e77a269Jae Seo            mStopCastingButton.setTextColor(value.data);
28306991009e52024cbb3dde550988e06149e77a269Jae Seo        }
28406991009e52024cbb3dde550988e06149e77a269Jae Seo
2851ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mRouteNameTextView = (TextView) findViewById(R.id.mr_name);
2861ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mCloseButton = (ImageButton) findViewById(R.id.mr_close);
287b48ccf48b0281e69262fc643d0def898aede407bSungsoo Lim        mCloseButton.setOnClickListener(listener);
2881ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mCustomControlLayout = (FrameLayout) findViewById(R.id.mr_custom_control);
2892b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang        mDefaultControlLayout = (FrameLayout) findViewById(R.id.mr_default_control);
2907ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        mArtView = (ImageView) findViewById(R.id.mr_art);
2916051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
2928e1281a26eaabd6422fd84c11010134a0a9c3338Jaewan Kim        mMediaControlLayout = (LinearLayout) findViewById(R.id.mr_media_control);
2931ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mDividerView = findViewById(R.id.mr_control_divider);
2941ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
2951ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mPlaybackControl = (RelativeLayout) findViewById(R.id.mr_playback_control);
2961ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mTitleView = (TextView) findViewById(R.id.mr_control_title);
2971ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mSubtitleView = (TextView) findViewById(R.id.mr_control_subtitle);
2981ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mPlayPauseButton = (ImageButton) findViewById(R.id.mr_control_play_pause);
2991ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mPlayPauseButton.setOnClickListener(listener);
3001ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
3011ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mVolumeControl = (LinearLayout) findViewById(R.id.mr_volume_control);
3021ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mVolumeSlider = (SeekBar) findViewById(R.id.mr_volume_slider);
3031ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mVolumeSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
3041ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            private final Runnable mStopTrackingTouch = new Runnable() {
3051ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                @Override
3061ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                public void run() {
3071ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    if (mVolumeSliderTouched) {
3081ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        mVolumeSliderTouched = false;
3091ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        updateVolumeControl();
3101ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    }
3111ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
3121ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            };
3131ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
3141ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            @Override
3151ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            public void onStartTrackingTouch(SeekBar seekBar) {
3161ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                if (mVolumeSliderTouched) {
3171ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mVolumeSlider.removeCallbacks(mStopTrackingTouch);
3181ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                } else {
3191ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mVolumeSliderTouched = true;
3201ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
3211ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
3221ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
3231ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            @Override
3241ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            public void onStopTrackingTouch(SeekBar seekBar) {
3251ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                // Defer resetting mVolumeSliderTouched to allow the media route provider
3261ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                // a little time to settle into its new state and publish the final
3271ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                // volume update.
3281ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mVolumeSlider.postDelayed(mStopTrackingTouch, VOLUME_UPDATE_DELAY_MILLIS);
3291ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
3301ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
3311ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            @Override
3321ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
3331ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                if (fromUser) {
3341ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mRoute.requestSetVolume(progress);
3351ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
3361ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
3371ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        });
33886a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo        mVolumeSliderColor = MediaRouterThemeHelper.getVolumeSliderColor(getContext());
3390b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo        setVolumeSliderColor(getContext(), mVolumeSlider, mVolumeSliderColor);
3401ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
3411ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        TypedArray styledAttributes = getContext().obtainStyledAttributes(new int[] {
3421ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                R.attr.mediaRouteExpandGroupDrawable,
3431ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                R.attr.mediaRouteCollapseGroupDrawable
3441ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        });
3451ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        final Drawable expandGroupDrawable = styledAttributes.getDrawable(0);
3461ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        final Drawable collapseGroupDrawable = styledAttributes.getDrawable(1);
3471ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        styledAttributes.recycle();
3481ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
3497ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        mVolumeGroupList = (ListView)findViewById(R.id.mr_volume_group_list);
3501ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mGroupExpandCollapseButton = (ImageButton) findViewById(R.id.mr_group_expand_collapse);
3511ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mGroupExpandCollapseButton.setOnClickListener(new View.OnClickListener() {
3521ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            @Override
3531ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            public void onClick(View v) {
3542b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang                mIsGroupExpanded = !mIsGroupExpanded;
3552b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang                if (mIsGroupExpanded) {
3561ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mGroupExpandCollapseButton.setImageDrawable(collapseGroupDrawable);
3571ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mVolumeGroupList.setVisibility(View.VISIBLE);
3581ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mVolumeGroupList.setAdapter(
3591ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            new VolumeGroupAdapter(getContext(), getGroup().getRoutes()));
3601ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                } else {
3611ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mGroupExpandCollapseButton.setImageDrawable(expandGroupDrawable);
3621ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mVolumeGroupList.setVisibility(View.GONE);
3631ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
3640d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                updateControlFrameLayout();
3651ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
3661ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        });
36711417b1cfde8f1749905f2d735623af9214148afJeff Brown
3687ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        mCustomControlView = onCreateMediaControlView(savedInstanceState);
3697ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        if (mCustomControlView != null) {
3701ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            mCustomControlLayout.addView(mCustomControlView);
3711ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            mCustomControlLayout.setVisibility(View.VISIBLE);
3727ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim            mArtView.setVisibility(View.GONE);
37311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
37486a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo        mCreated = true;
3757ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        update();
37611417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
37711417b1cfde8f1749905f2d735623af9214148afJeff Brown
37811417b1cfde8f1749905f2d735623af9214148afJeff Brown    @Override
37911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void onAttachedToWindow() {
38011417b1cfde8f1749905f2d735623af9214148afJeff Brown        super.onAttachedToWindow();
381e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        mAttachedToWindow = true;
38211417b1cfde8f1749905f2d735623af9214148afJeff Brown
38311417b1cfde8f1749905f2d735623af9214148afJeff Brown        mRouter.addCallback(MediaRouteSelector.EMPTY, mCallback,
38411417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
385e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        setMediaSession(mRouter.getMediaSessionToken());
38611417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
38711417b1cfde8f1749905f2d735623af9214148afJeff Brown
38811417b1cfde8f1749905f2d735623af9214148afJeff Brown    @Override
38911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void onDetachedFromWindow() {
39011417b1cfde8f1749905f2d735623af9214148afJeff Brown        mRouter.removeCallback(mCallback);
391e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        setMediaSession(null);
392e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik        mAttachedToWindow = false;
39311417b1cfde8f1749905f2d735623af9214148afJeff Brown        super.onDetachedFromWindow();
39411417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
39511417b1cfde8f1749905f2d735623af9214148afJeff Brown
39611417b1cfde8f1749905f2d735623af9214148afJeff Brown    @Override
39711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public boolean onKeyDown(int keyCode, KeyEvent event) {
398ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
399ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
400ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown            mRoute.requestUpdateVolume(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ? -1 : 1);
401ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown            return true;
40211417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
40311417b1cfde8f1749905f2d735623af9214148afJeff Brown        return super.onKeyDown(keyCode, event);
40411417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
40511417b1cfde8f1749905f2d735623af9214148afJeff Brown
40611417b1cfde8f1749905f2d735623af9214148afJeff Brown    @Override
40711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public boolean onKeyUp(int keyCode, KeyEvent event) {
408ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
409ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown                || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
410ce8cbd614a64f94e54b936d66ecd14b6c1282ddfJeff Brown            return true;
41111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
41211417b1cfde8f1749905f2d735623af9214148afJeff Brown        return super.onKeyUp(keyCode, event);
41311417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
41411417b1cfde8f1749905f2d735623af9214148afJeff Brown
4157ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim    private void update() {
41611417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!mRoute.isSelected() || mRoute.isDefault()) {
41711417b1cfde8f1749905f2d735623af9214148afJeff Brown            dismiss();
4187ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim            return;
41911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
42094be6100218126ce6a08bf1f56209578500b361fRoboErik        if (!mCreated) {
4217ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim            return;
42294be6100218126ce6a08bf1f56209578500b361fRoboErik        }
4232ef36d857302c5cd738c7c8bdec53d31feebebbaJeff Brown
4247ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        mRouteNameTextView.setText(mRoute.getName());
42506991009e52024cbb3dde550988e06149e77a269Jae Seo        mDisconnectButton.setVisibility(mRoute.canDisconnect() ? View.VISIBLE : View.GONE);
42694be6100218126ce6a08bf1f56209578500b361fRoboErik
4277ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim        if (mCustomControlView == null) {
428005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            if (mFetchArtTask != null) {
429005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                mFetchArtTask.cancel(true);
43066937fc7dfe22c8c505bf735781d1f5b62f08e34Dongwon Kang            }
431005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            mFetchArtTask = new FetchArtTask();
432005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            mFetchArtTask.execute();
433f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah        }
4341ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        updateVolumeControl();
4351ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        updatePlaybackControl();
4361ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    }
4371ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
4381ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private void updateVolumeControl() {
4391ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        if (!mVolumeSliderTouched) {
4401ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            if (isVolumeControlAvailable()) {
4411ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mVolumeControl.setVisibility(View.VISIBLE);
4421ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mVolumeSlider.setMax(mRoute.getVolumeMax());
4431ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mVolumeSlider.setProgress(mRoute.getVolume());
4441ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                if (USE_GROUP) {
4451ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    if (getGroup() == null) {
4461ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        mGroupExpandCollapseButton.setVisibility(View.GONE);
4471ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    } else {
4481ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        mGroupExpandCollapseButton.setVisibility(View.VISIBLE);
4491ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        VolumeGroupAdapter adapter =
4501ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                                (VolumeGroupAdapter) mVolumeGroupList.getAdapter();
4511ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        if (adapter != null) {
4521ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            adapter.notifyDataSetChanged();
4531ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        }
4541ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    }
4551ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
4561ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            } else {
4571ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mVolumeControl.setVisibility(View.GONE);
4581ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
459b7b02f5cac8b760805d98ddbfe03e0730bee6709Sungsoo Lim            updateControlLayoutHeight();
4601ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        }
4611ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    }
4621ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
4631ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    private void updatePlaybackControl() {
4641ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        if (mCustomControlView == null && (mDescription != null || mState != null)) {
4651ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            mPlaybackControl.setVisibility(View.VISIBLE);
4661ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            CharSequence title = mDescription == null ? null : mDescription.getTitle();
4671ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            boolean hasTitle = !TextUtils.isEmpty(title);
4681ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
4691ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            CharSequence subtitle = mDescription == null ? null : mDescription.getSubtitle();
4701ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            boolean hasSubtitle = !TextUtils.isEmpty(subtitle);
4711ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
4721ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            if (!hasTitle && !hasSubtitle) {
4731ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                if (mRoute.getPresentationDisplayId()
4741ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
4751ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    // The user is currently casting screen.
4761ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mTitleView.setText(R.string.mr_controller_casting_screen);
4771ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                } else {
4781ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mTitleView.setText((mState == null
4791ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            || mState.getState() == PlaybackStateCompat.STATE_NONE)
4801ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                                    ? R.string.mr_controller_no_media_selected
4811ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                                    : R.string.mr_controller_no_info_available);
4821ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
4831ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mTitleView.setVisibility(View.VISIBLE);
4841ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mSubtitleView.setVisibility(View.GONE);
4851ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            } else {
4861ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mTitleView.setText(title);
4871ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mTitleView.setVisibility(hasTitle ? View.VISIBLE : View.GONE);
4881ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mSubtitleView.setText(subtitle);
4891ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                mSubtitleView.setVisibility(hasSubtitle ? View.VISIBLE : View.GONE);
4901ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
4911ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            if (mState != null) {
4921ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                boolean isPlaying = mState.getState() == PlaybackStateCompat.STATE_BUFFERING
4931ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        || mState.getState() == PlaybackStateCompat.STATE_PLAYING;
4941ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                boolean supportsPlay = (mState.getActions() & (PlaybackStateCompat.ACTION_PLAY
4951ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
4961ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                boolean supportsPause = (mState.getActions() & (PlaybackStateCompat.ACTION_PAUSE
4971ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                        | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
4981ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                if (isPlaying && supportsPause) {
4991ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setVisibility(View.VISIBLE);
5001ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource(
5011ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            getContext(), R.attr.mediaRoutePauseDrawable));
5021ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setContentDescription(getContext().getResources()
5031ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            .getText(R.string.mr_controller_pause));
5041ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                } else if (!isPlaying && supportsPlay) {
5051ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setVisibility(View.VISIBLE);
5061ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setImageResource(MediaRouterThemeHelper.getThemeResource(
5071ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            getContext(), R.attr.mediaRoutePlayDrawable));
5081ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setContentDescription(getContext().getResources()
5091ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                            .getText(R.string.mr_controller_play));
5101ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                } else {
5111ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                    mPlayPauseButton.setVisibility(View.GONE);
5121ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                }
5131ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            }
5141ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        } else {
5151ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            mPlaybackControl.setVisibility(View.GONE);
5161ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        }
517b7b02f5cac8b760805d98ddbfe03e0730bee6709Sungsoo Lim        updateControlLayoutHeight();
5181ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim    }
5191ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim
520b7b02f5cac8b760805d98ddbfe03e0730bee6709Sungsoo Lim    private void updateControlLayoutHeight() {
521b7b02f5cac8b760805d98ddbfe03e0730bee6709Sungsoo Lim        // TODO: Update the top and bottom padding of the control layout according to the display
5221ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        // height.
5231ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim        mDividerView.setVisibility((mVolumeControl.getVisibility() == View.VISIBLE
5241ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                && mPlaybackControl.getVisibility() == View.VISIBLE)
5251ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                ? View.VISIBLE : View.GONE);
5268e1281a26eaabd6422fd84c11010134a0a9c3338Jaewan Kim        mMediaControlLayout.setVisibility((mVolumeControl.getVisibility() == View.GONE
5271ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                && mPlaybackControl.getVisibility() == View.GONE)
5281ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                ? View.GONE : View.VISIBLE);
529f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    }
530f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah
5312b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang    private void updateControlFrameLayout() {
5322b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang        int height;
5332b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang        if (mArtView.getVisibility() == View.GONE) {
5342b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang            height = LinearLayout.LayoutParams.WRAP_CONTENT;
5352b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang        } else {
536f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo            measureAndGetDecorView();
5370d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang            if (!mIsGroupExpanded) {
5380d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                height = mArtView.getMeasuredHeight() + mMediaControlLayout.getMeasuredHeight();
5392b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang            } else {
5400d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                if (mVolumeGroupList.getAdapter().getCount() <= 2
5410d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                        && mArtView.getMeasuredHeight() > mVolumeGroupList.getMeasuredHeight()) {
5420d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                    // Push the controls up and partially cover the artwork.
5430d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                    height = mArtView.getMeasuredHeight() + mMediaControlLayout.getMeasuredHeight()
5440d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                            - mVolumeGroupList.getMeasuredHeight();
5450d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                } else {
5460d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                    // Completely cover the artwork.
5470d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                    height = LinearLayout.LayoutParams.WRAP_CONTENT;
5480d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                }
5492b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang            }
5502b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang        }
5510d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang        mDefaultControlLayout.setLayoutParams(new LinearLayout.LayoutParams(
5520d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                LinearLayout.LayoutParams.FILL_PARENT, height));
5532b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang    }
5542b274bd3e06bd33c9b2a0851256afcda94d96395Dongwon Kang
555f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    private boolean isVolumeControlAvailable() {
556f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah        return mVolumeControlEnabled && mRoute.getVolumeHandling() ==
557f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah                MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE;
558f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah    }
559f1ca2f34f5efed21a49d92dd06e8e51be104c10bPrameet Shah
560f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo    private View measureAndGetDecorView() {
5610d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang        View decorView = getWindow().getDecorView();
562f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        decorView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
5630d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang        return decorView;
5640d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang    }
5650d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang
56613d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim    private void updateArtView() {
56713d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        if (!(mArtView.getDrawable() instanceof BitmapDrawable)) {
568bfb90b6717bb46535d747aa50045a206965542d0Jae Seo            mArtView.setVisibility(View.GONE);
56913d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            return;
57013d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        }
57113d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap();
57213d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        if (art == null) {
573bfb90b6717bb46535d747aa50045a206965542d0Jae Seo            mArtView.setVisibility(View.GONE);
57413d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            return;
57513d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        }
57613d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        int desiredArtHeight = getDesiredArtHeight(art.getWidth(), art.getHeight());
57713d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
578f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        View decorView = measureAndGetDecorView();
57913d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        // Show art if and only if it fits in the screen.
58013d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        if (mArtView.getVisibility() == View.GONE) {
58113d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            if (decorView.getMeasuredHeight() + desiredArtHeight <= displayMetrics.heightPixels) {
58213d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                mArtView.setVisibility(View.VISIBLE);
58313d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                mArtView.setMaxHeight(desiredArtHeight);
58413d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            }
58513d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        } else {
58613d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            if (decorView.getMeasuredHeight() - mArtView.getMeasuredHeight() + desiredArtHeight
58713d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    <= displayMetrics.heightPixels) {
58813d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                mArtView.setMaxHeight(desiredArtHeight);
58913d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            } else {
59013d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                mArtView.setVisibility(View.GONE);
59113d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            }
59213d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        }
59313d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim    }
59413d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim
5950b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo    private static void setVolumeSliderColor(Context context, SeekBar volumeSlider, int color) {
59686a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo        volumeSlider.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
59786a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo        if (Build.VERSION.SDK_INT >= 16) {
59886a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo            SeekBarJellybean.getThumb(volumeSlider).setColorFilter(color, PorterDuff.Mode.SRC_IN);
5990b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo        } else {
6000b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo            // In case getThumb() isn't available, use the thumb drawable from AppCompat.
6010b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo            Drawable thumb =
6020b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo                    context.getResources().getDrawable(R.drawable.abc_seekbar_thumb_material);
6030b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo            thumb.setColorFilter(color, PorterDuff.Mode.SRC_IN);
6040b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo            volumeSlider.setThumb(thumb);
60586a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo        }
60686a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo    }
60786a802c8d09983d8725cccf59217e65d3e0c6373Jae Seo
60813d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim    /**
60913d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim     * Returns desired art height to fit into controller dialog.
61013d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim     */
61113d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim    private int getDesiredArtHeight(int originalWidth, int originalHeight) {
61213d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        if (originalWidth >= originalHeight) {
61313d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            // For landscape art, fit width to dialog width.
614f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo            return (int) ((float) mDialogContentWidth * originalHeight / originalWidth + 0.5f);
61513d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        }
61613d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        // For portrait art, fit height to 16:9 ratio case's height.
617f4575634e9f3c2b61c2e6bceaeed6cabb378803fJae Seo        return (int) ((float) mDialogContentWidth * 9 / 16 + 0.5f);
61813d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim    }
61913d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim
62011417b1cfde8f1749905f2d735623af9214148afJeff Brown    private final class MediaRouterCallback extends MediaRouter.Callback {
62111417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
62211417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
62311417b1cfde8f1749905f2d735623af9214148afJeff Brown            update();
62411417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
62511417b1cfde8f1749905f2d735623af9214148afJeff Brown
62611417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
62711417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo route) {
62811417b1cfde8f1749905f2d735623af9214148afJeff Brown            update();
62911417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
63011417b1cfde8f1749905f2d735623af9214148afJeff Brown
63111417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
63211417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo route) {
63311417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (route == mRoute) {
6341ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim                updateVolumeControl();
63594be6100218126ce6a08bf1f56209578500b361fRoboErik            }
63694be6100218126ce6a08bf1f56209578500b361fRoboErik        }
63794be6100218126ce6a08bf1f56209578500b361fRoboErik    }
63894be6100218126ce6a08bf1f56209578500b361fRoboErik
63994be6100218126ce6a08bf1f56209578500b361fRoboErik    private final class MediaControllerCallback extends MediaControllerCompat.Callback {
64094be6100218126ce6a08bf1f56209578500b361fRoboErik        @Override
64194be6100218126ce6a08bf1f56209578500b361fRoboErik        public void onSessionDestroyed() {
64294be6100218126ce6a08bf1f56209578500b361fRoboErik            if (mMediaController != null) {
64394be6100218126ce6a08bf1f56209578500b361fRoboErik                mMediaController.unregisterCallback(mControllerCallback);
64494be6100218126ce6a08bf1f56209578500b361fRoboErik                mMediaController = null;
64594be6100218126ce6a08bf1f56209578500b361fRoboErik            }
64694be6100218126ce6a08bf1f56209578500b361fRoboErik        }
64794be6100218126ce6a08bf1f56209578500b361fRoboErik
64894be6100218126ce6a08bf1f56209578500b361fRoboErik        @Override
64994be6100218126ce6a08bf1f56209578500b361fRoboErik        public void onPlaybackStateChanged(PlaybackStateCompat state) {
65094be6100218126ce6a08bf1f56209578500b361fRoboErik            mState = state;
65194be6100218126ce6a08bf1f56209578500b361fRoboErik            update();
65294be6100218126ce6a08bf1f56209578500b361fRoboErik        }
65394be6100218126ce6a08bf1f56209578500b361fRoboErik
65494be6100218126ce6a08bf1f56209578500b361fRoboErik        @Override
65594be6100218126ce6a08bf1f56209578500b361fRoboErik        public void onMetadataChanged(MediaMetadataCompat metadata) {
65694be6100218126ce6a08bf1f56209578500b361fRoboErik            mDescription = metadata == null ? null : metadata.getDescription();
65794be6100218126ce6a08bf1f56209578500b361fRoboErik            update();
65894be6100218126ce6a08bf1f56209578500b361fRoboErik        }
65994be6100218126ce6a08bf1f56209578500b361fRoboErik    }
66094be6100218126ce6a08bf1f56209578500b361fRoboErik
66194be6100218126ce6a08bf1f56209578500b361fRoboErik    private final class ClickListener implements View.OnClickListener {
66294be6100218126ce6a08bf1f56209578500b361fRoboErik        @Override
66394be6100218126ce6a08bf1f56209578500b361fRoboErik        public void onClick(View v) {
66494be6100218126ce6a08bf1f56209578500b361fRoboErik            int id = v.getId();
66506991009e52024cbb3dde550988e06149e77a269Jae Seo            if (id == BUTTON_STOP_RES_ID || id == BUTTON_DISCONNECT_RES_ID) {
66694be6100218126ce6a08bf1f56209578500b361fRoboErik                if (mRoute.isSelected()) {
66706991009e52024cbb3dde550988e06149e77a269Jae Seo                    mRouter.unselect(id == BUTTON_STOP_RES_ID ?
66894be6100218126ce6a08bf1f56209578500b361fRoboErik                            MediaRouter.UNSELECT_REASON_STOPPED :
66994be6100218126ce6a08bf1f56209578500b361fRoboErik                            MediaRouter.UNSELECT_REASON_DISCONNECTED);
67094be6100218126ce6a08bf1f56209578500b361fRoboErik                }
67194be6100218126ce6a08bf1f56209578500b361fRoboErik                dismiss();
6727ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim            } else if (id == R.id.mr_control_play_pause) {
67394be6100218126ce6a08bf1f56209578500b361fRoboErik                if (mMediaController != null && mState != null) {
67494be6100218126ce6a08bf1f56209578500b361fRoboErik                    if (mState.getState() == PlaybackStateCompat.STATE_PLAYING) {
67594be6100218126ce6a08bf1f56209578500b361fRoboErik                        mMediaController.getTransportControls().pause();
67694be6100218126ce6a08bf1f56209578500b361fRoboErik                    } else {
67794be6100218126ce6a08bf1f56209578500b361fRoboErik                        mMediaController.getTransportControls().play();
67894be6100218126ce6a08bf1f56209578500b361fRoboErik                    }
67994be6100218126ce6a08bf1f56209578500b361fRoboErik                }
6801ce169ba80c2047846dca5ee8c723faadcd1da86Sungsoo Lim            } else if (id == R.id.mr_close) {
681b48ccf48b0281e69262fc643d0def898aede407bSungsoo Lim                dismiss();
68211417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
68311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
68411417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
685005482649154d0970693b3967241eb4d2cb22003Jaewan Kim
6866051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo    private class VolumeGroupAdapter extends ArrayAdapter<MediaRouter.RouteInfo> {
6870e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo        final static float DISABLED_ALPHA = .3f;
6880e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo
6896051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        final OnSeekBarChangeListener mOnSeekBarChangeListener = new OnSeekBarChangeListener() {
6906051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            @Override
6916051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
6926051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                if (fromUser) {
6936051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                    int position = (int) seekBar.getTag();
6946051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                    getGroup().getRouteAt(position).requestSetVolume(progress);
6956051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                }
6966051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            }
6976051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
6986051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            @Override
6996051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            public void onStartTrackingTouch(SeekBar seekBar) {
7006051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                // TODO: Implement
7016051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            }
7026051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
7036051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            @Override
7046051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            public void onStopTrackingTouch(SeekBar seekBar) {
7056051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                // TODO: Implement
7066051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            }
7076051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        };
7086051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
7096051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        public VolumeGroupAdapter(Context context, List<MediaRouter.RouteInfo> objects) {
7106051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            super(context, 0, objects);
7116051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        }
7126051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
7136051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        @Override
7146051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        public View getView(final int position, View convertView, ViewGroup parent) {
7156051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            View v = convertView;
7166051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            if (v == null) {
7176051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                v = LayoutInflater.from(getContext()).inflate(
718a09c7b30d3a5c53281db955eabce171e1aec6feaSungsoo Lim                        R.layout.mr_controller_volume_item, null);
7190b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo                setVolumeSliderColor(getContext(), (SeekBar) v.findViewById(R.id.mr_volume_slider),
7200b0446251d63e184a1bb9ee62ddac0b344e7e571Jae Seo                        mVolumeSliderColor);
7216051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            }
7226051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            MediaRouter.RouteInfo route = getItem(position);
7236051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            if (route != null) {
7240e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                boolean isEnabled = route.isEnabled();
7250e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo
7267ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim                TextView routeName = (TextView) v.findViewById(R.id.mr_name);
7270e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                routeName.setEnabled(isEnabled);
7280e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                routeName.setText(route.getName());
7296051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
7307ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim                SeekBar volumeSlider = (SeekBar) v.findViewById(R.id.mr_volume_slider);
7310e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                volumeSlider.setEnabled(isEnabled);
7326051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo                volumeSlider.setTag(position);
7330e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                if (isEnabled) {
7340e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                    if (route.getVolumeHandling()
7350e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                            == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) {
7360e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                        volumeSlider.setMax(route.getVolumeMax());
7370e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                        volumeSlider.setProgress(route.getVolume());
7380e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                        volumeSlider.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
7390e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                    } else {
7400e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                        volumeSlider.setMax(100);
7410e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                        volumeSlider.setProgress(100);
7420e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                        volumeSlider.setEnabled(false);
7430e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                    }
7440e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                }
745418c688351713e9349227262163eef752a8e7ce6Jae Seo                if (Build.VERSION.SDK_INT >= 16) {
746418c688351713e9349227262163eef752a8e7ce6Jae Seo                    SeekBarJellybean.getThumb(volumeSlider).mutate().setAlpha(isEnabled ? 255 : 0);
747418c688351713e9349227262163eef752a8e7ce6Jae Seo                    // TODO: Still see an artifact even though the thumb is transparent. Remove it.
748418c688351713e9349227262163eef752a8e7ce6Jae Seo                }
7490e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo
7500e36aa71ffed511f5ad6b0a24e75ccf651e43c40Jae Seo                ImageView volumeItemIcon =
7517ff59b631bd798bdf0c6647d1279a08a33c62977Sungsoo Lim                        (ImageView) v.findViewById(R.id.mr_volume_item_icon);
75277367b4a1871417198d0399d9ad074314c758567Jae Seo                volumeItemIcon.setAlpha(isEnabled ? 255 : (int) (255 * DISABLED_ALPHA));
7536051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            }
7546051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo            return v;
7556051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo        }
7566051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo    }
7576051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo
758005482649154d0970693b3967241eb4d2cb22003Jaewan Kim    private class FetchArtTask extends AsyncTask<Void, Void, Bitmap> {
7598bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        final Bitmap mIconBitmap;
7608bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        final Uri mIconUri;
7618bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        int mBackgroundColor;
7628bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo
7638bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        FetchArtTask() {
7648bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            mIconBitmap = mDescription == null ? null : mDescription.getIconBitmap();
7658bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            mIconUri = mDescription == null ? null : mDescription.getIconUri();
7668bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        }
7678bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo
7688bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        @Override
7698bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        protected void onPreExecute() {
7708bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            if (mArtIconBitmap == mIconBitmap && mArtIconUri == mIconUri) {
7718bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                // Already handled the current art.
7728bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                cancel(true);
7738bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            }
7748bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo        }
775005482649154d0970693b3967241eb4d2cb22003Jaewan Kim
776005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        @Override
777005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        protected Bitmap doInBackground(Void... arg) {
77813d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            Bitmap art = null;
7798bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            if (mIconBitmap != null) {
7808bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                art = mIconBitmap;
7818bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            } else if (mIconUri != null) {
7828bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                String scheme = mIconUri.getScheme();
783005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                if (!(ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
784005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        || ContentResolver.SCHEME_CONTENT.equals(scheme)
785005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        || ContentResolver.SCHEME_FILE.equals(scheme))) {
786005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    Log.w(TAG, "Icon Uri should point to local resources.");
787005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    return null;
788005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                }
789005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                BufferedInputStream stream = null;
790005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                try {
791005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    stream = new BufferedInputStream(
7928bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                            getContext().getContentResolver().openInputStream(mIconUri));
793005482649154d0970693b3967241eb4d2cb22003Jaewan Kim
79413d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    // Query art size.
795005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    BitmapFactory.Options options = new BitmapFactory.Options();
796005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    options.inJustDecodeBounds = true;
797005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    BitmapFactory.decodeStream(stream, null, options);
79813d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    if (options.outWidth == 0 || options.outHeight == 0) {
79913d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                        return null;
80013d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    }
80113d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    // Rewind the stream in order to restart art decoding.
802005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    try {
803005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        stream.reset();
804005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    } catch (IOException e) {
805005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        // Failed to rewind the stream, try to reopen it.
806005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        stream.close();
807005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        stream = new BufferedInputStream(getContext().getContentResolver()
8088bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                                .openInputStream(mIconUri));
809005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    }
81013d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    // Calculate required size to decode the art and possibly resize it.
811005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    options.inJustDecodeBounds = false;
81213d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    int reqHeight = getDesiredArtHeight(options.outWidth, options.outHeight);
81313d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    int ratio = options.outHeight / reqHeight;
814005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    options.inSampleSize = Math.max(1, Integer.highestOneBit(ratio));
815005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    if (isCancelled()) {
816005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        return null;
817005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    }
81813d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim                    art = BitmapFactory.decodeStream(stream, null, options);
819005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                } catch (IOException e){
8208bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                    Log.w(TAG, "Unable to open: " + mIconUri, e);
821005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                } finally {
822005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    if (stream != null) {
823005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        try {
824005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                            stream.close();
825005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        } catch (IOException e) {
826005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                        }
827005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                    }
828005482649154d0970693b3967241eb4d2cb22003Jaewan Kim                }
829005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            }
830145e93b13dd025b278665494e7be0c15b8df5986Jaewan Kim            if (art != null && art.getWidth() < art.getHeight()) {
831145e93b13dd025b278665494e7be0c15b8df5986Jaewan Kim                // Portrait art requires background color.
832145e93b13dd025b278665494e7be0c15b8df5986Jaewan Kim                Palette palette = new Palette.Builder(art).maximumColorCount(1).generate();
833145e93b13dd025b278665494e7be0c15b8df5986Jaewan Kim                mBackgroundColor = (palette.getSwatches() == null)
834145e93b13dd025b278665494e7be0c15b8df5986Jaewan Kim                        ? 0 : palette.getSwatches().get(0).getRgb();
835005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            }
83613d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim            return art;
837005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        }
838005482649154d0970693b3967241eb4d2cb22003Jaewan Kim
839005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        @Override
840005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        protected void onCancelled() {
841005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            mFetchArtTask = null;
842005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        }
843005482649154d0970693b3967241eb4d2cb22003Jaewan Kim
844005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        @Override
84513d6fb598d2270bed0dbcdb8d66d012382780f53Jaewan Kim        protected void onPostExecute(Bitmap art) {
846005482649154d0970693b3967241eb4d2cb22003Jaewan Kim            mFetchArtTask = null;
8478bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            if (mArtIconBitmap != mIconBitmap || mArtIconUri != mIconUri) {
8488bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                mArtIconBitmap = mIconBitmap;
8498bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                mArtIconUri = mIconUri;
8508bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo
8518bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                mArtView.setImageBitmap(art);
8528bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                mArtView.setBackgroundColor(mBackgroundColor);
8538bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo                updateArtView();
8540d35e8e4de45b751f979d40aa9ffad3875ba9f66Dongwon Kang                updateControlFrameLayout();
8558bc60d9260a4b31356fceb31264a47b55f6114f4Jae Seo            }
856005482649154d0970693b3967241eb4d2cb22003Jaewan Kim        }
857005482649154d0970693b3967241eb4d2cb22003Jaewan Kim    }
8586051d8374c90da4826698b66ed7cb13ebc2c65bfJae Seo}
859