package com.wasu.cs.widget.mediacontrol; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; import android.content.Context; import android.media.MediaPlayer; import android.util.AttributeSet; import android.util.Property; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import com.wasu.comp.videoview.IMediaControl; import com.wasu.comp.videoview.IMediaListener; import com.wasu.cs.model.ILiveAssets; import com.wasu.cs.widget.videoview.WasuLivePlayerView; import java.util.List; /** * 直播播控 * * @author zhangrm * */ public class LiveMediaController extends FrameLayout implements IMediaListener, IMediaController { public static final int DISPLAY_CHANNEL_PANEL_CONTROLLER = 1 << 0; public static final int DISPLAY_CHANNEL_HEADER = 1 << 1; public static final int DISPLAY_PROMPT_BUFFER = 1 << 2; public static final int DISPLAY_VOLUME_CONTROLLER = 1 << 3; public static final int DISPLAY_VOLUME_MUTE_CONTROLLER = 1 << 4; public static final int DISPLAY_PROMPT_EXIT_AD = 1 << 5; private static int ID_BASE = 600000000; public static int generateID() { return ++ID_BASE; } /** * 控件持续显示时间 */ private static final int DURATION_SHOW = 5000; /** * 动画持续时间 */ private static final int DURATION_ANIM = 500; private static AccelerateInterpolator sAccelerator = new AccelerateInterpolator(); private static DecelerateInterpolator sDecelerator = new DecelerateInterpolator(); public enum AnimationDirection { LEFT, RIGHT, UP, DOWN, CENTER } /** * 默认显示选项 */ public int displayOption = DISPLAY_CHANNEL_PANEL_CONTROLLER | DISPLAY_CHANNEL_HEADER | DISPLAY_PROMPT_BUFFER | DISPLAY_VOLUME_CONTROLLER | DISPLAY_VOLUME_MUTE_CONTROLLER; public void setDisplayOption(int displayOption) { this.displayOption = displayOption; } public boolean hasOption(int option) { return (displayOption & option) == option; } public void setExcludeOption(int option) { this.displayOption = displayOption & ~option; } // ///////////////////////////////////////////////////////////////////////////////////////// /** * 播放器和数据是否已经准备好 */ private boolean isReady; public boolean isReady() { return isReady; } public void setReady(boolean isReady) { this.isReady = isReady; } /** * 播放器 */ private IMediaControl player; public IMediaControl getPlayer() { return player; } public void setPlayer(IMediaControl player) { this.player = player; } /** * 资产信息 */ private ILiveAssets liveInfo; public ILiveAssets getLiveInfo() { return liveInfo; } public void setLiveInfo(ILiveAssets liveInfo) { this.liveInfo = liveInfo; } /** * 当前播放类型 */ private int playType; public int getPlayType() { return playType; } public void setPlayType(int playType) { this.playType = playType; } /** * 当前频道id */ private String channelId; public String getChannelId() { return channelId; } public void setChannelId(String channelId) { this.channelId = channelId; } /** * 隐藏当前view */ private Runnable hideCurrentViewTask = new Runnable() { @Override public void run() { hideCurrentView(); } }; // ///////////////////////////////////////////////////////////////////////////////////////// /** * 当前显示的子view */ private IMediaControllerChildView currentView; private ChannelHeader channelHeader; private VolumeController volumeController; private VolumeMuteController volumeMuteController; private ChannelPanelController channelPanelController; private PromptBuffer promptBuffer; private PromptExitAD promptExitAD; public LiveMediaController(Context context) { super(context); init(context); } public LiveMediaController(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public LiveMediaController(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { setFocusable(false); setFocusableInTouchMode(false); channelHeader = new ChannelHeader(context); volumeController = new VolumeController(context); volumeMuteController = new VolumeMuteController(context); channelPanelController = new ChannelPanelController(context); promptBuffer = new PromptBuffer(context); promptExitAD = new PromptExitAD(context); // FIXME 加一个无用的view,确保其他的view加进来时得到绘制 addView(new View(context)); } public void clear() { removeCallbacks(hideCurrentViewTask); channelHeader.clear(); volumeController.clear(); volumeMuteController.clear(); channelPanelController.clear(); promptBuffer.clear(); isReady = false; } /** * 隐藏当前view */ public void hideCurrentView() { if (currentView == null || !currentView.isAutoHide()) { return; } if (currentView.getRelativeViews() != null) { for (IMediaControllerChildView v : (List) currentView.getRelativeViews()) { setVisibility(false, true, v.getId()); } currentView.getRelativeViews().clear(); } setVisibility(false, currentView.getId()); } public void handleFullScreen(boolean isFullScreen) { promptBuffer.handleFullScreen(isFullScreen); } public boolean addViewWithAnimInfo(View view, int gravity, AnimationDirection direction) { return addViewWithAnimInfo(view, gravity, direction, -1); } public boolean addViewWithAnimInfo(View view, int gravity, AnimationDirection direction, int index) { if (NO_ID == view.getId()) { return false; } View existing = findViewById(view.getId()); if (null != existing) { return false; } AnimInfo info = new AnimInfo(); info.direction = direction; info.gravity = gravity; view.setTag(info); if ((gravity & Gravity.TOP) == Gravity.TOP || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, gravity); addView(view, index, lp); } else if ((gravity & Gravity.LEFT) == Gravity.LEFT || (gravity & Gravity.RIGHT) == Gravity.RIGHT) { LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT, gravity); addView(view, index, lp); } else if ((gravity & Gravity.CENTER) == Gravity.CENTER) { LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, gravity); addView(view, index, lp); } else { LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, gravity); addView(view, index, lp); } prepareViewAnimation(view); return true; } public void prepareViewAnimation(View view) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } if (null != info.animIn && null != info.animOut) { return; } switch (info.direction) { case CENTER: setupViewAnimationCenter(view); break; case LEFT: setupViewAnimationLeft(view); break; case RIGHT: setupViewAnimationRight(view); break; case DOWN: setupViewAnimationDown(view); break; case UP: setupViewAnimationUp(view); break; } } @SuppressLint("NewApi") private void setupViewAnimationCenter(View view) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } view.setAlpha(0f); setViewAnimationIn(view, View.ALPHA, 1f, 0l); setViewAnimationOut(view, View.ALPHA, 0f, 0l); } @SuppressLint("NewApi") private void setupViewAnimationLeft(View view) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams(); if ((info.gravity & Gravity.LEFT) == Gravity.LEFT) { lp.leftMargin = getMeasuredWidth(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_X, -getMeasuredWidth()); setViewAnimationOut(view, View.TRANSLATION_X, 0); } else if ((info.gravity & Gravity.RIGHT) == Gravity.RIGHT) { lp.rightMargin = -view.getMeasuredWidth(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_X, -view.getMeasuredWidth()); setViewAnimationOut(view, View.TRANSLATION_X, 0); } } @SuppressLint("NewApi") private void setupViewAnimationRight(View view) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams(); if ((info.gravity & Gravity.LEFT) == Gravity.LEFT) { lp.leftMargin = -view.getMeasuredWidth(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_X, view.getMeasuredWidth()); setViewAnimationOut(view, View.TRANSLATION_X, 0); } else if ((info.gravity & Gravity.RIGHT) == Gravity.RIGHT) { lp.rightMargin = getMeasuredWidth(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_X, getMeasuredWidth()); setViewAnimationOut(view, View.TRANSLATION_X, 0); } } @SuppressLint("NewApi") private void setupViewAnimationUp(View view) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams(); if ((info.gravity & Gravity.TOP) == Gravity.TOP) { lp.topMargin = getMeasuredHeight(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_Y, -getMeasuredHeight()); setViewAnimationOut(view, View.TRANSLATION_Y, 0); } else if ((info.gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { lp.bottomMargin = -view.getMeasuredHeight(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_Y, -view.getMeasuredHeight()); setViewAnimationOut(view, View.TRANSLATION_Y, 0); } } @SuppressLint("NewApi") private void setupViewAnimationDown(View view) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams(); if ((info.gravity & Gravity.TOP) == Gravity.TOP) { lp.topMargin = -view.getMeasuredHeight(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_Y, view.getMeasuredHeight()); setViewAnimationOut(view, View.TRANSLATION_Y, 0); } else if ((info.gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { lp.bottomMargin = getMeasuredHeight(); updateViewLayout(view, lp); setViewAnimationIn(view, View.TRANSLATION_Y, getMeasuredHeight()); setViewAnimationOut(view, View.TRANSLATION_Y, 0); } } @SuppressLint("NewApi") private void setViewAnimationIn(final View view, Property property, float value) { setViewAnimationIn(view, property, value, DURATION_ANIM); } @SuppressLint("NewApi") private void setViewAnimationIn(final View view, Property property, float value, long duration) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } PropertyValuesHolder pvh = PropertyValuesHolder.ofFloat(property, value); info.animIn = ObjectAnimator.ofPropertyValuesHolder(view, pvh); info.animIn.setDuration(duration); info.animIn.setInterpolator(sDecelerator); info.animIn.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { if (view instanceof IMediaControllerChildView) { IMediaControllerChildView child = (IMediaControllerChildView) view; child.onShow(LiveMediaController.this); } } }); } @SuppressLint("NewApi") private void setViewAnimationOut(final View view, Property property, float value) { setViewAnimationOut(view, property, value, DURATION_ANIM); } @SuppressLint("NewApi") private void setViewAnimationOut(final View view, Property property, float value, long duration) { AnimInfo info = (AnimInfo) view.getTag(); if (null == info) { return; } PropertyValuesHolder pvh = PropertyValuesHolder.ofFloat(property, value); info.animOut = ObjectAnimator.ofPropertyValuesHolder(view, pvh); info.animOut.setDuration(DURATION_ANIM); info.animOut.setInterpolator(sAccelerator); info.animOut.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (view instanceof IMediaControllerChildView) { IMediaControllerChildView child = (IMediaControllerChildView) view; child.onHide(LiveMediaController.this); } removeView(view); } }); } public void setVisibility(boolean show, int viewId) { setVisibility(show, false, viewId); } public void notifyVolumeChanged(int volume) { volumeMuteController.volumeChanged(volume); } /** * 显示或隐藏view * * @param show * 是否显示 * @param isUnattached * 是否是独立的view,不与其他view互斥 * @param viewId */ public void setVisibility(boolean show, boolean isUnattached, int viewId) { if (show) { showView(viewId, isUnattached); } else { hideView(viewId, isUnattached); } } private void showView(int viewId, final boolean isUnattached) { final View view = findViewById(viewId); if (null != view) { AnimInfo info = (AnimInfo) view.getTag(); if (null != info) { if (info.animOut.isRunning()) { return; } info.animIn.start(); } player.addObserver((IMediaControllerChildView) view); if (!isUnattached) { currentView = (IMediaControllerChildView) view; removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } } private void hideView(int viewId, final boolean isUnattached) { final View view = findViewById(viewId); if (null != view) { AnimInfo info = (AnimInfo) view.getTag(); if (null != info) { if (info.animIn.isRunning()) { return; } info.animOut.start(); } if (!isUnattached) { currentView = null; } } } @Override public boolean dispatchKeyEvent(KeyEvent event) { boolean ret = false; if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_BACK: ret = handleKeyBack(event); break; case KeyEvent.KEYCODE_MENU: handleKeyMenu(event); ret = true; break; case KeyEvent.KEYCODE_DPAD_UP: handleKeyUp(event); ret = true; break; case KeyEvent.KEYCODE_DPAD_DOWN: handleKeyDown(event); ret = true; break; case KeyEvent.KEYCODE_DPAD_LEFT: handleKeyLeft(event); ret = true; break; case KeyEvent.KEYCODE_DPAD_RIGHT: handleKeyRight(event); ret = true; break; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: handleKeyEnter(event); ret = true; break; case KeyEvent.KEYCODE_VOLUME_UP: handleKeyVolumeUp(event); ret = true; break; case KeyEvent.KEYCODE_VOLUME_DOWN: handleKeyVolumeDown(event); ret = true; break; case KeyEvent.KEYCODE_VOLUME_MUTE: handleKeyVolumeMute(event); ret = true; break; } } return ret; } /** * 处理返回键 * * @param event */ private boolean handleKeyBack(KeyEvent event) { if (currentView != null) { if (!currentView.onShield(event)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } return true; } else { if (hasOption(DISPLAY_PROMPT_EXIT_AD)) { addViewWithAnimInfo(promptExitAD, Gravity.CENTER, AnimationDirection.CENTER); setVisibility(true, promptExitAD.getId()); } else { return false; } } return true; } /** * 处理确定键 * * @param event */ private void handleKeyEnter(KeyEvent event) { if (!isReady()) { if (currentView != null && currentView instanceof PromptExitAD) { if (currentView.onShield(event)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } } return; } if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { if (currentView instanceof PromptExitAD || currentView instanceof PromptPauseAD) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } else { removeCallbacks(hideCurrentViewTask); hideCurrentView(); if (hasOption(DISPLAY_CHANNEL_PANEL_CONTROLLER)) { addViewWithAnimInfo(channelPanelController, Gravity.LEFT, AnimationDirection.RIGHT); setVisibility(true, channelPanelController.getId()); } } } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理菜单键 * * @param event */ private void handleKeyMenu(KeyEvent event) { if (!isReady()) { return; } } /** * 处理方向上键 * * @param event */ private void handleKeyUp(KeyEvent event) { if (!isReady()) { return; } if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); if (getPlayer() instanceof WasuLivePlayerView) { String preChannelId = liveInfo.getPreviousChannelId(channelId); if (channelId.equals(preChannelId)) { return; } getPlayer().stopPlayback(); getContext().getSharedPreferences(WasuLivePlayerView.CONFIG_FILE, Context.MODE_PRIVATE).edit().putString(WasuLivePlayerView.CHANNEL_KEY, preChannelId).commit(); ((WasuLivePlayerView) getPlayer()).play(playType, preChannelId); } } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理方向下键 * * @param event */ private void handleKeyDown(KeyEvent event) { if (!isReady()) { return; } if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); if (getPlayer() instanceof WasuLivePlayerView) { String nextChannelId = liveInfo.getNextChannelId(channelId); if (channelId.equals(nextChannelId)) { return; } getPlayer().stopPlayback(); getContext().getSharedPreferences(WasuLivePlayerView.CONFIG_FILE, Context.MODE_PRIVATE).edit().putString(WasuLivePlayerView.CHANNEL_KEY, nextChannelId).commit(); ((WasuLivePlayerView) getPlayer()).play(playType, nextChannelId); } } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理方向左键 * * @param event */ private void handleKeyLeft(KeyEvent event) { if (!isReady()) { if (currentView != null && currentView instanceof PromptExitAD) { if (!currentView.onShield(event)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } } return; } if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理方向右键 * * @param event */ private void handleKeyRight(KeyEvent event) { if (!isReady()) { if (currentView != null && currentView instanceof PromptExitAD) { if (!currentView.onShield(event)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } } return; } if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理音量加键 * * @param event */ private void handleKeyVolumeUp(KeyEvent event) { if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); if (hasOption(DISPLAY_VOLUME_CONTROLLER)) { addViewWithAnimInfo(volumeController, Gravity.CENTER, AnimationDirection.CENTER); setVisibility(true, volumeController.getId()); } } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理音量减键 * * @param event */ private void handleKeyVolumeDown(KeyEvent event) { if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); if (hasOption(DISPLAY_VOLUME_CONTROLLER)) { addViewWithAnimInfo(volumeController, Gravity.CENTER, AnimationDirection.CENTER); setVisibility(true, volumeController.getId()); } } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } /** * 处理静音键 * * @param event */ private void handleKeyVolumeMute(KeyEvent event) { if ((currentView != null && !currentView.onShield(event)) || (currentView == null)) { removeCallbacks(hideCurrentViewTask); hideCurrentView(); if (hasOption(DISPLAY_VOLUME_MUTE_CONTROLLER)) { addViewWithAnimInfo(volumeMuteController, Gravity.CENTER, AnimationDirection.CENTER); setVisibility(true, volumeMuteController.getId()); } } else { removeCallbacks(hideCurrentViewTask); postDelayed(hideCurrentViewTask, DURATION_SHOW); } } public boolean showBufferView() { boolean ret = false; if (hasOption(DISPLAY_PROMPT_BUFFER)) { ret = addViewWithAnimInfo(promptBuffer, Gravity.CENTER, AnimationDirection.CENTER, 0); setVisibility(true, true, promptBuffer.getId()); } return ret; } public void hideBufferView() { setVisibility(false, true, promptBuffer.getId()); } public void showChannelHeaderView() { if (hasOption(DISPLAY_CHANNEL_HEADER)) { addViewWithAnimInfo(channelHeader, Gravity.TOP, AnimationDirection.DOWN); setVisibility(true, true, channelHeader.getId()); } } public void hideChannelHeaderView() { setVisibility(false, true, channelHeader.getId()); } @Override public void onAdStatusChanged(int arg0, int arg1) { } @Override public void onCompletion(MediaPlayer arg0) { } @Override public void onError(MediaPlayer arg0, int arg1, int arg2) { } @Override public void onInfo(MediaPlayer arg0, int arg1, int arg2) { switch (arg1) { case MediaPlayer.MEDIA_INFO_BUFFERING_START: showBufferView(); break; case MediaPlayer.MEDIA_INFO_BUFFERING_END: hideBufferView(); break; } } @Override public void onPause(MediaPlayer arg0) { } @Override public void onPrepareComplete(MediaPlayer arg0) { hideBufferView(); showChannelHeaderView(); postDelayed(new Runnable() { @Override public void run() { hideChannelHeaderView(); } }, 5000); } @Override public void onPreparing(MediaPlayer arg0) { showBufferView(); } @Override public void onProgress(int arg0, int arg1, int arg2) { } @Override public void onResume(MediaPlayer arg0) { } @Override public void onSeekComplete(MediaPlayer arg0) { } @Override public void onSeeking(MediaPlayer arg0) { } @Override public void onStart(MediaPlayer arg0) { } @Override public void onStatusChanged(MediaPlayer arg0, int arg1) { if (arg1 == IMediaListener.STATE_PREPARING || arg1 == IMediaListener.STATE_BUFFERING) { showBufferView(); } else { if (player != null && player.isInPlaybackState()) { hideBufferView(); } } } @Override public void onStop(MediaPlayer arg0) { } @Override public void onWasuError(int arg0, String arg1) { } @Override public void onWasuPlayLimit(int arg0, String arg1) { if (arg0 == IMediaListener.WASU_PLAY_IN_PREVIEW) { } else if (arg0 == IMediaListener.WASU_PLAY_FREE) { } } public class AnimInfo { public int gravity; public AnimationDirection direction; public ObjectAnimator animIn; public ObjectAnimator animOut; } }