TransportControllerActivity.java revision 1337277f8f8802064f42bed895695758f40b50d9
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.example.android.supportv4.media; 18 19import com.example.android.supportv4.R; 20 21import android.app.ActionBar; 22import android.content.Context; 23import android.media.MediaPlayer; 24import android.os.Handler; 25import android.util.AttributeSet; 26import android.view.KeyEvent; 27import android.view.View; 28 29import android.app.Activity; 30import android.net.Uri; 31import android.os.Bundle; 32import android.widget.VideoView; 33 34import android.support.v4.media.TransportController; 35 36public class TransportControllerActivity extends Activity { 37 38 /** 39 * TODO: Set the path variable to a streaming video URL or a local media 40 * file path. 41 */ 42 private Content mContent; 43 private TransportController mTransportController; 44 private MediaController mMediaController; 45 46 /** 47 * Handle media buttons to start/stop video playback. Real implementations 48 * will probably handle more buttons, like skip and fast-forward. 49 */ 50 TransportController.Callbacks mTransportCallbacks = new TransportController.Callbacks() { 51 public boolean onMediaButtonDown(int keyCode, KeyEvent event) { 52 switch (keyCode) { 53 case TransportController.KEYCODE_MEDIA_PLAY: 54 mMediaPlayerControl.start(); 55 return true; 56 case TransportController.KEYCODE_MEDIA_PAUSE: 57 case KeyEvent.KEYCODE_MEDIA_STOP: 58 mMediaPlayerControl.pause(); 59 return true; 60 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 61 case KeyEvent.KEYCODE_HEADSETHOOK: 62 if (mContent.isPlaying()) { 63 mMediaPlayerControl.pause(); 64 } else { 65 mMediaPlayerControl.start(); 66 } 67 } 68 return true; 69 } 70 }; 71 72 /** 73 * Handle actions from on-screen media controls. Most of these are simple re-directs 74 * to the VideoView; some we need to capture to update our state. 75 */ 76 MediaController.MediaPlayerControl mMediaPlayerControl 77 = new MediaController.MediaPlayerControl() { 78 79 @Override 80 public void start() { 81 mTransportController.startPlaying(); 82 mContent.start(); 83 } 84 85 @Override 86 public void pause() { 87 mTransportController.pausePlaying(); 88 mContent.pause(); 89 } 90 91 @Override 92 public int getDuration() { 93 return mContent.getDuration(); 94 } 95 96 @Override 97 public int getCurrentPosition() { 98 return mContent.getCurrentPosition(); 99 } 100 101 @Override 102 public void seekTo(int pos) { 103 mContent.seekTo(pos); 104 } 105 106 @Override 107 public boolean isPlaying() { 108 return mContent.isPlaying(); 109 } 110 111 @Override 112 public int getBufferPercentage() { 113 return mContent.getBufferPercentage(); 114 } 115 116 @Override 117 public boolean canPause() { 118 return mContent.canPause(); 119 } 120 121 @Override 122 public boolean canSeekBackward() { 123 return mContent.canSeekBackward(); 124 } 125 126 @Override 127 public boolean canSeekForward() { 128 return mContent.canSeekForward(); 129 } 130 }; 131 132 /** 133 * This is the actual video player. It is the top-level content of 134 * the activity's view hierarchy, going under the status bar and nav 135 * bar areas. 136 */ 137 public static class Content extends VideoView implements 138 View.OnSystemUiVisibilityChangeListener, View.OnClickListener, 139 ActionBar.OnMenuVisibilityListener, MediaPlayer.OnPreparedListener, 140 MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener { 141 Activity mActivity; 142 TransportController mTransportController; 143 MediaController mMediaController; 144 boolean mAddedMenuListener; 145 boolean mMenusOpen; 146 boolean mPaused; 147 boolean mNavVisible; 148 int mLastSystemUiVis; 149 150 Runnable mNavHider = new Runnable() { 151 @Override public void run() { 152 setNavVisibility(false); 153 } 154 }; 155 156 Runnable mProgressUpdater = new Runnable() { 157 @Override public void run() { 158 mMediaController.updateProgress(); 159 getHandler().postDelayed(this, 1000); 160 } 161 }; 162 163 public Content(Context context, AttributeSet attrs) { 164 super(context, attrs); 165 setOnSystemUiVisibilityChangeListener(this); 166 setOnClickListener(this); 167 setOnPreparedListener(this); 168 setOnCompletionListener(this); 169 setOnErrorListener(this); 170 } 171 172 public void init(Activity activity, TransportController transportController, 173 MediaController mediaController) { 174 // This called by the containing activity to supply the surrounding 175 // state of the video player that it will interact with. 176 mActivity = activity; 177 mTransportController = transportController; 178 mMediaController = mediaController; 179 pause(); 180 } 181 182 @Override protected void onAttachedToWindow() { 183 super.onAttachedToWindow(); 184 if (mActivity != null) { 185 mAddedMenuListener = true; 186 mActivity.getActionBar().addOnMenuVisibilityListener(this); 187 } 188 } 189 190 @Override protected void onDetachedFromWindow() { 191 super.onDetachedFromWindow(); 192 if (mAddedMenuListener) { 193 mActivity.getActionBar().removeOnMenuVisibilityListener(this); 194 } 195 mNavVisible = false; 196 } 197 198 @Override public void onSystemUiVisibilityChange(int visibility) { 199 // Detect when we go out of nav-hidden mode, to clear our state 200 // back to having the full UI chrome up. Only do this when 201 // the state is changing and nav is no longer hidden. 202 int diff = mLastSystemUiVis ^ visibility; 203 mLastSystemUiVis = visibility; 204 if ((diff&SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0 205 && (visibility&SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) { 206 setNavVisibility(true); 207 } 208 } 209 210 @Override protected void onWindowVisibilityChanged(int visibility) { 211 super.onWindowVisibilityChanged(visibility); 212 213 // When we become visible or invisible, play is paused. 214 pause(); 215 } 216 217 @Override public void onClick(View v) { 218 // Clicking anywhere makes the navigation visible. 219 setNavVisibility(true); 220 } 221 222 @Override public void onMenuVisibilityChanged(boolean isVisible) { 223 mMenusOpen = isVisible; 224 setNavVisibility(true); 225 } 226 227 @Override 228 public void onPrepared(MediaPlayer mp) { 229 mMediaController.setEnabled(true); 230 } 231 232 @Override 233 public void onCompletion(MediaPlayer mp) { 234 mTransportController.pausePlaying(); 235 pause(); 236 } 237 238 @Override 239 public boolean onError(MediaPlayer mp, int what, int extra) { 240 mTransportController.pausePlaying(); 241 pause(); 242 return false; 243 } 244 245 @Override public void start() { 246 super.start(); 247 mPaused = false; 248 setKeepScreenOn(true); 249 setNavVisibility(true); 250 mMediaController.refresh(); 251 scheduleProgressUpdater(); 252 } 253 254 @Override public void pause() { 255 super.pause(); 256 mPaused = true; 257 setKeepScreenOn(false); 258 setNavVisibility(true); 259 mMediaController.refresh(); 260 scheduleProgressUpdater(); 261 } 262 263 void scheduleProgressUpdater() { 264 Handler h = getHandler(); 265 if (h != null) { 266 if (mNavVisible && !mPaused) { 267 h.removeCallbacks(mProgressUpdater); 268 h.post(mProgressUpdater); 269 } else { 270 h.removeCallbacks(mProgressUpdater); 271 } 272 } 273 } 274 275 void setNavVisibility(boolean visible) { 276 int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 277 | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 278 | SYSTEM_UI_FLAG_LAYOUT_STABLE; 279 if (!visible) { 280 newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN 281 | SYSTEM_UI_FLAG_HIDE_NAVIGATION; 282 } 283 284 // If we are now visible, schedule a timer for us to go invisible. 285 if (visible) { 286 Handler h = getHandler(); 287 if (h != null) { 288 h.removeCallbacks(mNavHider); 289 if (!mMenusOpen && !mPaused) { 290 // If the menus are open or play is paused, we will not auto-hide. 291 h.postDelayed(mNavHider, 3000); 292 } 293 } 294 } 295 296 // Set the new desired visibility. 297 setSystemUiVisibility(newVis); 298 mNavVisible = visible; 299 mMediaController.setVisibility(visible ? VISIBLE : INVISIBLE); 300 scheduleProgressUpdater(); 301 } 302 } 303 304 @Override 305 public void onCreate(Bundle icicle) { 306 super.onCreate(icicle); 307 setContentView(R.layout.videoview); 308 309 // Find the video player in our UI. 310 mContent = (Content) findViewById(R.id.content); 311 312 // Create and initialize the media control UI. 313 mMediaController = (MediaController) findViewById(R.id.media_controller); 314 mMediaController.setMediaPlayer(mMediaPlayerControl); 315 316 // Create transport controller to control video, giving the callback 317 // interface to receive actions from. 318 mTransportController = new TransportController(this, mTransportCallbacks); 319 320 // We're just playing a built-in demo video. 321 mContent.init(this, mTransportController, mMediaController); 322 mContent.setVideoURI(Uri.parse("android.resource://" + getPackageName() + 323 "/" + R.raw.videoviewdemo)); 324 } 325 326 @Override 327 public boolean dispatchKeyEvent(KeyEvent event) { 328 // We first dispatch keys to the transport controller -- we want it 329 // to get to consume any media keys rather than letting whoever has focus 330 // in the view hierarchy to potentially eat it. 331 if (mTransportController.dispatchKeyEvent(event)) { 332 return true; 333 } 334 335 return super.dispatchKeyEvent(event); 336 } 337} 338