1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.content.browser; 6 7 import android.content.Context; 8 import android.graphics.Color; 9 import android.view.KeyEvent; 10 import android.view.MotionEvent; 11 import android.view.SurfaceHolder; 12 import android.view.SurfaceView; 13 import android.view.View; 14 import android.widget.MediaController; 15 16 /** 17 * Adds Android media controller to ContentVideoView. 18 * The sole purpose of this class is to maintain legacy behavior while we test 19 * the blink-based media controller. 20 * https://code.google.com/p/chromium/issues/detail?id=331966 21 */ 22 public class ContentVideoViewLegacy extends ContentVideoView { 23 private FullScreenMediaController mMediaController; 24 private boolean mCanPause; 25 private boolean mCanSeekBackward; 26 private boolean mCanSeekForward; 27 private int mCurrentBufferPercentage; 28 private MediaControlsVisibilityListener mListener; 29 30 /** 31 * A listener for changes in the MediaController visibility. 32 */ 33 public interface MediaControlsVisibilityListener { 34 /** 35 * Callback for when the visibility of the media controls changes. 36 * 37 * @param shown true if the media controls are shown to the user, false otherwise 38 */ onMediaControlsVisibilityChanged(boolean shown)39 public void onMediaControlsVisibilityChanged(boolean shown); 40 } 41 42 private static class FullScreenMediaController extends MediaController { 43 44 final View mVideoView; 45 final MediaControlsVisibilityListener mListener; 46 47 /** 48 * @param context The context. 49 * @param video The full screen video container view. 50 * @param listener A listener that listens to the visibility of media controllers. 51 */ FullScreenMediaController( Context context, View video, MediaControlsVisibilityListener listener)52 public FullScreenMediaController( 53 Context context, 54 View video, 55 MediaControlsVisibilityListener listener) { 56 super(context); 57 mVideoView = video; 58 mListener = listener; 59 } 60 61 @Override show()62 public void show() { 63 super.show(); 64 if (mListener != null) mListener.onMediaControlsVisibilityChanged(true); 65 if (mVideoView != null) { 66 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); 67 } 68 } 69 70 @Override hide()71 public void hide() { 72 if (mVideoView != null) { 73 mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 74 } 75 if (mListener != null) mListener.onMediaControlsVisibilityChanged(false); 76 super.hide(); 77 } 78 } 79 ContentVideoViewLegacy(Context context, long nativeContentVideoView, ContentVideoViewClient client)80 ContentVideoViewLegacy(Context context, long nativeContentVideoView, 81 ContentVideoViewClient client) { 82 super(context, nativeContentVideoView, client); 83 setBackgroundColor(Color.BLACK); 84 mCurrentBufferPercentage = 0; 85 } 86 87 @Override showContentVideoView()88 protected void showContentVideoView() { 89 SurfaceView surfaceView = getSurfaceView(); 90 surfaceView.setZOrderOnTop(true); 91 surfaceView.setOnKeyListener(new OnKeyListener() { 92 @Override 93 public boolean onKey(View v, int keyCode, KeyEvent event) { 94 boolean isKeyCodeSupported = ( 95 keyCode != KeyEvent.KEYCODE_BACK && 96 keyCode != KeyEvent.KEYCODE_VOLUME_UP && 97 keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && 98 keyCode != KeyEvent.KEYCODE_VOLUME_MUTE && 99 keyCode != KeyEvent.KEYCODE_CALL && 100 keyCode != KeyEvent.KEYCODE_MENU && 101 keyCode != KeyEvent.KEYCODE_SEARCH && 102 keyCode != KeyEvent.KEYCODE_ENDCALL); 103 if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { 104 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || 105 keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { 106 if (isPlaying()) { 107 pause(); 108 mMediaController.show(); 109 } else { 110 start(); 111 mMediaController.hide(); 112 } 113 return true; 114 } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { 115 if (!isPlaying()) { 116 start(); 117 mMediaController.hide(); 118 } 119 return true; 120 } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP 121 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { 122 if (isPlaying()) { 123 pause(); 124 mMediaController.show(); 125 } 126 return true; 127 } else { 128 toggleMediaControlsVisiblity(); 129 } 130 } else if (keyCode == KeyEvent.KEYCODE_BACK && 131 event.getAction() == KeyEvent.ACTION_UP) { 132 exitFullscreen(false); 133 return true; 134 } else if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_SEARCH) { 135 return true; 136 } 137 return false; 138 } 139 }); 140 setOnTouchListener(new OnTouchListener() { 141 @Override 142 public boolean onTouch(View v, MotionEvent event) { 143 if (isInPlaybackState() && mMediaController != null && 144 event.getAction() == MotionEvent.ACTION_DOWN) { 145 toggleMediaControlsVisiblity(); 146 } 147 return true; 148 } 149 }); 150 surfaceView.setFocusable(true); 151 surfaceView.setFocusableInTouchMode(true); 152 surfaceView.requestFocus(); 153 super.showContentVideoView(); 154 } 155 156 @Override onMediaPlayerError(int errorType)157 public void onMediaPlayerError(int errorType) { 158 super.onMediaPlayerError(errorType); 159 if (errorType == MEDIA_ERROR_INVALID_CODE) return; 160 if (mMediaController != null) mMediaController.hide(); 161 } 162 163 @Override onBufferingUpdate(int percent)164 protected void onBufferingUpdate(int percent) { 165 super.onBufferingUpdate(percent); 166 mCurrentBufferPercentage = percent; 167 } 168 169 @Override onUpdateMediaMetadata( int videoWidth, int videoHeight, int duration, boolean canPause, boolean canSeekBack, boolean canSeekForward)170 protected void onUpdateMediaMetadata( 171 int videoWidth, 172 int videoHeight, 173 int duration, 174 boolean canPause, 175 boolean canSeekBack, 176 boolean canSeekForward) { 177 super.onUpdateMediaMetadata(videoWidth, videoHeight, duration, 178 canPause, canSeekBack, canSeekForward); 179 mCanPause = canPause; 180 mCanSeekBackward = canSeekBack; 181 mCanSeekForward = canSeekForward; 182 183 if (mMediaController == null) return; 184 185 mMediaController.setEnabled(true); 186 // If paused , should show the controller forever. 187 if (isPlaying()) { 188 mMediaController.show(); 189 } else { 190 mMediaController.show(0); 191 } 192 } 193 194 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)195 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 196 super.surfaceChanged(holder, format, width, height); 197 SurfaceView surfaceView = getSurfaceView(); 198 surfaceView.setFocusable(true); 199 surfaceView.setFocusableInTouchMode(true); 200 if (isInPlaybackState() && mMediaController != null) { 201 mMediaController.show(); 202 } 203 } 204 205 @Override openVideo()206 protected void openVideo() { 207 super.openVideo(); 208 209 mCurrentBufferPercentage = 0; 210 211 if (mMediaController != null) return; 212 mMediaController = new FullScreenMediaController(getContext(), this, mListener); 213 mMediaController.setMediaPlayer(new MediaController.MediaPlayerControl() { 214 @Override public boolean canPause() { return mCanPause; } 215 @Override public boolean canSeekBackward() { return mCanSeekBackward; } 216 @Override public boolean canSeekForward() { return mCanSeekForward; } 217 @Override public int getAudioSessionId() { return 0; } 218 @Override public int getBufferPercentage() { return mCurrentBufferPercentage; } 219 220 @Override 221 public int getCurrentPosition() { 222 return ContentVideoViewLegacy.this.getCurrentPosition(); 223 } 224 225 @Override 226 public int getDuration() { 227 return ContentVideoViewLegacy.this.getDuration(); 228 } 229 230 @Override 231 public boolean isPlaying() { 232 return ContentVideoViewLegacy.this.isPlaying(); 233 } 234 235 @Override 236 public void pause() { 237 ContentVideoViewLegacy.this.pause(); 238 } 239 240 @Override 241 public void seekTo(int pos) { 242 ContentVideoViewLegacy.this.seekTo(pos); 243 } 244 245 @Override 246 public void start() { 247 ContentVideoViewLegacy.this.start(); 248 } 249 }); 250 mMediaController.setAnchorView(getSurfaceView()); 251 mMediaController.setEnabled(false); 252 } 253 254 @Override onCompletion()255 protected void onCompletion() { 256 super.onCompletion(); 257 if (mMediaController != null) { 258 mMediaController.hide(); 259 } 260 } 261 262 @Override onTrackballEvent(MotionEvent ev)263 public boolean onTrackballEvent(MotionEvent ev) { 264 if (isInPlaybackState() && mMediaController != null) { 265 toggleMediaControlsVisiblity(); 266 } 267 return false; 268 } 269 toggleMediaControlsVisiblity()270 private void toggleMediaControlsVisiblity() { 271 if (mMediaController.isShowing()) { 272 mMediaController.hide(); 273 } else { 274 mMediaController.show(); 275 } 276 } 277 278 @Override destroyContentVideoView(boolean nativeViewDestroyed)279 protected void destroyContentVideoView(boolean nativeViewDestroyed) { 280 if (mMediaController != null) { 281 mMediaController.setEnabled(false); 282 mMediaController.hide(); 283 mMediaController = null; 284 } 285 super.destroyContentVideoView(nativeViewDestroyed); 286 } 287 288 @Override onTouchEvent(MotionEvent ev)289 public boolean onTouchEvent(MotionEvent ev) { 290 return true; 291 } 292 293 /** 294 * Sets the MediaControlsVisibilityListener that wants to listen to visibility change events. 295 * 296 * @param listener the listener to send the events to. 297 */ setListener(MediaControlsVisibilityListener listener)298 public void setListener(MediaControlsVisibilityListener listener) { 299 mListener = listener; 300 } 301 302 } 303