/* * Copyright (C) 2015 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.libraries.cast.companionlibrary.cast.dialog.video; import static com.google.android.libraries.cast.companionlibrary.utils.LogUtils.LOGE; import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaMetadata; import com.google.android.gms.cast.MediaStatus; import com.google.android.libraries.cast.companionlibrary.R; import com.google.android.libraries.cast.companionlibrary.cast.VideoCastManager; import com.google.android.libraries.cast.companionlibrary.cast.callbacks.VideoCastConsumerImpl; import com.google.android.libraries.cast.companionlibrary.cast.exceptions.CastException; import com.google.android.libraries.cast.companionlibrary.cast.exceptions.NoConnectionException; import com.google.android.libraries.cast.companionlibrary.cast.exceptions.TransientNetworkDisconnectionException; import com.google.android.libraries.cast.companionlibrary.utils.FetchBitmapTask; import com.google.android.libraries.cast.companionlibrary.utils.LogUtils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.MediaRouteControllerDialog; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; /** * A custom {@link MediaRouteControllerDialog} that provides an album art, a play/pause button and * the ability to take user to the target activity when the album art is tapped. */ public class VideoMediaRouteControllerDialog extends MediaRouteControllerDialog { private static final String TAG = LogUtils.makeLogTag(VideoMediaRouteControllerDialog.class); private ImageView mIcon; private ImageView mPausePlay; private TextView mTitle; private TextView mSubTitle; private TextView mEmptyText; private ProgressBar mLoading; private Uri mIconUri; private VideoCastManager mCastManager; protected int mState; private VideoCastConsumerImpl mCastConsumerImpl; private Drawable mPauseDrawable; private Drawable mPlayDrawable; private Drawable mStopDrawable; private Context mContext; private View mIconContainer; private View mTextContainer; private FetchBitmapTask mFetchBitmap; private int mStreamType; public VideoMediaRouteControllerDialog(Context context, int theme) { super(context, theme); } /** * Creates a new VideoMediaRouteControllerDialog with the given context. */ public VideoMediaRouteControllerDialog(Context context) { super(context, R.style.CCLCastDialog); try { this.mContext = context; mCastManager = VideoCastManager.getInstance(); mState = mCastManager.getPlaybackStatus(); mCastConsumerImpl = new VideoCastConsumerImpl() { @Override public void onRemoteMediaPlayerStatusUpdated() { mState = mCastManager.getPlaybackStatus(); updatePlayPauseState(mState); } /* * (non-Javadoc) * @see * com.google.android.libraries.cast.companionlibrary.cast.VideoCastConsumerImpl * #onMediaChannelMetadataUpdated() */ @Override public void onRemoteMediaPlayerMetadataUpdated() { updateMetadata(); } }; mCastManager.addVideoCastConsumer(mCastConsumerImpl); mPauseDrawable = context.getResources() .getDrawable(R.drawable.ic_media_route_controller_pause); mPlayDrawable = context.getResources() .getDrawable(R.drawable.ic_media_route_controller_play); mStopDrawable = context.getResources() .getDrawable(R.drawable.ic_media_route_controller_stop); } catch (IllegalStateException e) { LOGE(TAG, "Failed to update the content of dialog", e); } } @Override protected void onStop() { if (mCastManager != null) { mCastManager.removeVideoCastConsumer(mCastConsumerImpl); mCastManager = null; } if (mFetchBitmap != null) { mFetchBitmap.cancel(true); mFetchBitmap = null; } super.onStop(); } /* * Hides/show the icon and metadata and play/pause if there is no media */ private void hideControls(boolean hide, int resId) { int visibility = hide ? View.GONE : View.VISIBLE; mIcon.setVisibility(visibility); mIconContainer.setVisibility(visibility); mTextContainer.setVisibility(visibility); mEmptyText.setText(resId == 0 ? R.string.ccl_no_media_info : resId); mEmptyText.setVisibility(hide ? View.VISIBLE : View.GONE); if (hide) { mPausePlay.setVisibility(visibility); } } private void updateMetadata() { MediaInfo info; try { info = mCastManager.getRemoteMediaInformation(); } catch (TransientNetworkDisconnectionException | NoConnectionException e) { hideControls(true, R.string.ccl_failed_no_connection_short); return; } if (info == null) { hideControls(true, R.string.ccl_no_media_info); return; } mStreamType = info.getStreamType(); hideControls(false, 0); MediaMetadata mm = info.getMetadata(); mTitle.setText(mm.getString(MediaMetadata.KEY_TITLE)); mSubTitle.setText(mm.getString(MediaMetadata.KEY_SUBTITLE)); setIcon(mm.hasImages() ? mm.getImages().get(0).getUrl() : null); } public void setIcon(Uri uri) { if (mIconUri != null && mIconUri.equals(uri)) { return; } mIconUri = uri; if (uri == null) { Bitmap bm = BitmapFactory.decodeResource( mContext.getResources(), R.drawable.album_art_placeholder); mIcon.setImageBitmap(bm); return; } if (mFetchBitmap != null) { mFetchBitmap.cancel(true); } mFetchBitmap = new FetchBitmapTask() { @Override protected void onPostExecute(Bitmap bitmap) { mIcon.setImageBitmap(bitmap); if (this == mFetchBitmap) { mFetchBitmap = null; } } }; mFetchBitmap.execute(mIconUri); } private void updatePlayPauseState(int state) { if (mPausePlay != null) { switch (state) { case MediaStatus.PLAYER_STATE_PLAYING: mPausePlay.setImageDrawable(getPauseStopDrawable()); adjustControlsVisibility(true); break; case MediaStatus.PLAYER_STATE_PAUSED: mPausePlay.setImageDrawable(mPlayDrawable); adjustControlsVisibility(true); break; case MediaStatus.PLAYER_STATE_IDLE: mPausePlay.setVisibility(View.INVISIBLE); setLoadingVisibility(false); if (mState == MediaStatus.PLAYER_STATE_IDLE && mCastManager.getIdleReason() == MediaStatus.IDLE_REASON_FINISHED) { hideControls(true, R.string.ccl_no_media_info); } else { switch (mStreamType) { case MediaInfo.STREAM_TYPE_BUFFERED: mPausePlay.setVisibility(View.INVISIBLE); setLoadingVisibility(false); break; case MediaInfo.STREAM_TYPE_LIVE: int idleReason = mCastManager.getIdleReason(); if (idleReason == MediaStatus.IDLE_REASON_CANCELED) { mPausePlay.setImageDrawable(mPlayDrawable); adjustControlsVisibility(true); } else { mPausePlay.setVisibility(View.INVISIBLE); setLoadingVisibility(false); } break; } } break; case MediaStatus.PLAYER_STATE_BUFFERING: adjustControlsVisibility(false); break; default: mPausePlay.setVisibility(View.INVISIBLE); setLoadingVisibility(false); } } } private Drawable getPauseStopDrawable() { switch (mStreamType) { case MediaInfo.STREAM_TYPE_BUFFERED: return mPauseDrawable; case MediaInfo.STREAM_TYPE_LIVE: return mStopDrawable; default: return mPauseDrawable; } } private void setLoadingVisibility(boolean show) { mLoading.setVisibility(show ? View.VISIBLE : View.GONE); } private void adjustControlsVisibility(boolean showPlayPause) { int visible = showPlayPause ? View.VISIBLE : View.INVISIBLE; mPausePlay.setVisibility(visible); setLoadingVisibility(!showPlayPause); } /** * Initializes this dialog's set of playback buttons and adds click listeners. */ @Override public View onCreateMediaControlView(Bundle savedInstanceState) { LayoutInflater inflater = getLayoutInflater(); View controls = inflater.inflate(R.layout.custom_media_route_controller_controls_dialog, null); loadViews(controls); mState = mCastManager.getPlaybackStatus(); updateMetadata(); updatePlayPauseState(mState); setUpCallbacks(); return controls; } private void setUpCallbacks() { mPausePlay.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mCastManager == null) { return; } try { adjustControlsVisibility(false); mCastManager.togglePlayback(); } catch (CastException e) { adjustControlsVisibility(true); LOGE(TAG, "Failed to toggle playback", e); } catch (TransientNetworkDisconnectionException | NoConnectionException e) { adjustControlsVisibility(true); LOGE(TAG, "Failed to toggle playback due to network issues", e); } } }); mIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showTargetActivity(); } }); mTextContainer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showTargetActivity(); } }); } private void showTargetActivity() { if (mCastManager != null && mCastManager.getTargetActivity() != null) { try { mCastManager.onTargetActivityInvoked(mContext); } catch (TransientNetworkDisconnectionException | NoConnectionException e) { LOGE(TAG, "Failed to start the target activity due to network issues", e); } cancel(); } } private void loadViews(View controls) { mIcon = (ImageView) controls.findViewById(R.id.iconView); mIconContainer = controls.findViewById(R.id.iconContainer); mTextContainer = controls.findViewById(R.id.textContainer); mPausePlay = (ImageView) controls.findViewById(R.id.playPauseView); mTitle = (TextView) controls.findViewById(R.id.titleView); mSubTitle = (TextView) controls.findViewById(R.id.subTitleView); mLoading = (ProgressBar) controls.findViewById(R.id.loadingView); mEmptyText = (TextView) controls.findViewById(R.id.emptyView); } }