package com.distantfuture.castcompanionlibrary.lib.utils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.Drawable; import android.net.Uri; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import com.distantfuture.castcompanionlibrary.lib.R; import com.distantfuture.castcompanionlibrary.lib.cast.VideoCastManager; import com.distantfuture.castcompanionlibrary.lib.cast.exceptions.CastException; import com.distantfuture.castcompanionlibrary.lib.cast.exceptions.NoConnectionException; import com.distantfuture.castcompanionlibrary.lib.cast.exceptions.OnFailedListener; import com.distantfuture.castcompanionlibrary.lib.cast.exceptions.TransientNetworkDisconnectionException; import com.google.android.gms.cast.MediaInfo; import com.google.android.gms.cast.MediaStatus; import java.net.URL; /** * A compound component that provides a superset of functionalities required for the global access * requirement. This component provides an image for the album art, a play/pause button, a seekbar * for trick-play with current time and duration and a mute/unmute button. Clients can add this * compound component to their layout xml and register that with the instance of * {@link VideoCastManager} by using the following pattern:<br/> * <p/> * <pre> * mMiniController = (MiniController) findViewById(R.id.miniController1); * mCastManager.addMiniController(mMiniController); * mMiniController.setOnMiniControllerChangedListener(mCastManager); * </pre> * <p/> * Then the {@link VideoCastManager} will manage the behavior, including its state and metadata and * interactions. */ public class MiniController extends RelativeLayout implements IMiniController { private static final String TAG = "MiniController"; protected ImageView mIcon; protected TextView mTitle; protected TextView mSubTitle; protected ImageView mPlayPause; protected ProgressBar mLoading; private OnMiniControllerChangedListener mListener; private Uri mIconUri; private Drawable mPauseDrawable; private Drawable mPlayDrawable; private View mContainer; private int mStreamType = MediaInfo.STREAM_TYPE_BUFFERED; private Drawable mStopDrawable; public MiniController(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.mini_controller, this); mPauseDrawable = getResources().getDrawable(R.drawable.ic_av_pause_light); mPlayDrawable = getResources().getDrawable(R.drawable.ic_av_play_light); mStopDrawable = getResources().getDrawable(R.drawable.ic_av_stop_light); loadViews(); setupCallbacks(); } public MiniController(Context context) { super(context); loadViews(); } /** * Sets the listener that should be notified when a relevant event is fired from this component. * Clients can register the {@link VideoCastManager} instance to be the default listener so it * can control the remote media playback. */ @Override public void setOnMiniControllerChangedListener(OnMiniControllerChangedListener listener) { if (null != listener) { this.mListener = listener; } } public void removeOnMiniControllerChangedListener(OnMiniControllerChangedListener listener) { if (null != listener && this.mListener == listener) { this.mListener = null; } } @Override public void setStreamType(int streamType) { this.mStreamType = streamType; } private void setupCallbacks() { mPlayPause.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (null != mListener) { setLoadingVisibility(true); try { mListener.onPlayPauseClicked(v); } catch (CastException e) { mListener.onFailed(R.string.failed_perform_action, -1); } catch (TransientNetworkDisconnectionException e) { mListener.onFailed(R.string.failed_no_connection_trans, -1); } catch (NoConnectionException e) { mListener.onFailed(R.string.failed_no_connection, -1); } } } }); mContainer.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (null != mListener) { setLoadingVisibility(false); try { mListener.onTargetActivityInvoked(mIcon.getContext()); } catch (Exception e) { mListener.onFailed(R.string.failed_perform_action, -1); } } } }); } private void setIcon(Bitmap bm) { mIcon.setImageBitmap(bm); } @Override public void setIcon(Uri uri) { if (null != mIconUri && mIconUri.equals(uri)) { return; } mIconUri = uri; new Thread(new Runnable() { Bitmap bm = null; @Override public void run() { try { URL imgUrl = new URL(mIconUri.toString()); bm = BitmapFactory.decodeStream(imgUrl.openStream()); } catch (Exception e) { CastUtils.LOGE(TAG, "setIcon(): Failed to load the image with url: " + mIconUri + ", using the default one", e); bm = BitmapFactory.decodeResource(getResources(), R.drawable.dummy_album_art); } mIcon.post(new Runnable() { @Override public void run() { setIcon(bm); } }); } }).start(); } @Override public void setTitle(String title) { mTitle.setText(title); } @Override public void setSubTitle(String subTitle) { mSubTitle.setText(subTitle); } @Override public void setPlaybackStatus(int state, int idleReason) { switch (state) { case MediaStatus.PLAYER_STATE_PLAYING: mPlayPause.setVisibility(View.VISIBLE); mPlayPause.setImageDrawable(getPauseStopButton()); setLoadingVisibility(false); break; case MediaStatus.PLAYER_STATE_PAUSED: mPlayPause.setVisibility(View.VISIBLE); mPlayPause.setImageDrawable(mPlayDrawable); setLoadingVisibility(false); break; case MediaStatus.PLAYER_STATE_IDLE: switch (mStreamType) { case MediaInfo.STREAM_TYPE_BUFFERED: mPlayPause.setVisibility(View.INVISIBLE); setLoadingVisibility(false); break; case MediaInfo.STREAM_TYPE_LIVE: if (idleReason == MediaStatus.IDLE_REASON_CANCELED) { mPlayPause.setVisibility(View.VISIBLE); mPlayPause.setImageDrawable(mPlayDrawable); setLoadingVisibility(false); } else { mPlayPause.setVisibility(View.INVISIBLE); setLoadingVisibility(false); } break; } break; case MediaStatus.PLAYER_STATE_BUFFERING: mPlayPause.setVisibility(View.INVISIBLE); setLoadingVisibility(true); break; default: mPlayPause.setVisibility(View.INVISIBLE); setLoadingVisibility(false); break; } } @Override public boolean isVisible() { return isShown(); } private void loadViews() { mIcon = (ImageView) findViewById(R.id.iconView); mTitle = (TextView) findViewById(R.id.titleView); mSubTitle = (TextView) findViewById(R.id.subTitleView); mPlayPause = (ImageView) findViewById(R.id.playPauseView); mLoading = (ProgressBar) findViewById(R.id.loadingView); mContainer = findViewById(R.id.bigContainer); } private void setLoadingVisibility(boolean show) { mLoading.setVisibility(show ? View.VISIBLE : View.GONE); } private Drawable getPauseStopButton() { switch (mStreamType) { case MediaInfo.STREAM_TYPE_BUFFERED: return mPauseDrawable; case MediaInfo.STREAM_TYPE_LIVE: return mStopDrawable; default: return mPauseDrawable; } } /** * The interface for a listener that will be called when user interacts with the * MiniController, like clicking on the play/pause button, etc. */ public interface OnMiniControllerChangedListener extends OnFailedListener { /** * Notification that user has clicked on the Play/Pause button * * @param v * @throws TransientNetworkDisconnectionException * @throws NoConnectionException * @throws CastException */ public void onPlayPauseClicked(View v) throws CastException, TransientNetworkDisconnectionException, NoConnectionException; /** * Notification that the user has clicked on the album art */ public void onTargetActivityInvoked(Context context) throws TransientNetworkDisconnectionException, NoConnectionException; } }