blob: d57b7391c4d083746507dc86493b5183d42d0b7d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.widget;
18
19import android.app.AlertDialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.content.Intent;
23import android.content.res.Resources;
Alan Viverettede213f72013-09-03 16:17:56 -070024import android.graphics.Canvas;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.media.AudioManager;
Lajos Molnaraf3098242013-08-15 20:56:53 -070026import android.media.MediaFormat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.media.MediaPlayer;
28import android.media.MediaPlayer.OnCompletionListener;
29import android.media.MediaPlayer.OnErrorListener;
James Donga0ba7942012-07-27 17:30:38 -070030import android.media.MediaPlayer.OnInfoListener;
Lajos Molnaraf3098242013-08-15 20:56:53 -070031import android.media.Metadata;
Lajos Molnar484ff7a92013-08-15 11:37:47 -070032import android.media.SubtitleController;
Alan Viveretted43daf32013-09-05 16:34:30 -070033import android.media.SubtitleTrack.RenderingWidget;
Lajos Molnar484ff7a92013-08-15 11:37:47 -070034import android.media.WebVttRenderer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.net.Uri;
Lajos Molnar29f51832013-09-20 08:45:31 -070036import android.os.Looper;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.util.AttributeSet;
38import android.util.Log;
Lajos Molnar484ff7a92013-08-15 11:37:47 -070039import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.view.KeyEvent;
41import android.view.MotionEvent;
42import android.view.SurfaceHolder;
43import android.view.SurfaceView;
44import android.view.View;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080045import android.view.accessibility.AccessibilityEvent;
46import android.view.accessibility.AccessibilityNodeInfo;
Owen Lina8381df2010-11-09 19:32:30 +080047import android.widget.MediaController.MediaPlayerControl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49import java.io.IOException;
Lajos Molnaraf3098242013-08-15 20:56:53 -070050import java.io.InputStream;
Andreas Huber25643002010-01-28 11:19:57 -080051import java.util.Map;
Lajos Molnaraf3098242013-08-15 20:56:53 -070052import java.util.Vector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54/**
55 * Displays a video file. The VideoView class
56 * can load images from various sources (such as resources or content
57 * providers), takes care of computing its measurement from the video so that
58 * it can be used in any layout manager, and provides various display options
Lajos Molnar7e11b162013-10-30 14:04:10 -070059 * such as scaling and tinting.<p>
60 *
61 * <em>Note: VideoView does not retain its full state when going into the
62 * background.</em> In particular, it does not restore the current play state,
63 * play position, selected tracks, or any subtitle tracks added via
64 * {@link #addSubtitleSource addSubtitleSource()}. Applications should
65 * save and restore these on their own in
66 * {@link android.app.Activity#onSaveInstanceState} and
67 * {@link android.app.Activity#onRestoreInstanceState}.<p>
68 * Also note that the audio session id (from {@link #getAudioSessionId}) may
69 * change from its previously returned value when the VideoView is restored.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 */
Lajos Molnar484ff7a92013-08-15 11:37:47 -070071public class VideoView extends SurfaceView
72 implements MediaPlayerControl, SubtitleController.Anchor {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -070073 private String TAG = "VideoView";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 // settable by the client
75 private Uri mUri;
Andreas Huber25643002010-01-28 11:19:57 -080076 private Map<String, String> mHeaders;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
James Dongfdf3ac62009-07-06 12:37:45 -070078 // all possible internal states
79 private static final int STATE_ERROR = -1;
80 private static final int STATE_IDLE = 0;
81 private static final int STATE_PREPARING = 1;
82 private static final int STATE_PREPARED = 2;
83 private static final int STATE_PLAYING = 3;
84 private static final int STATE_PAUSED = 4;
85 private static final int STATE_PLAYBACK_COMPLETED = 5;
86
87 // mCurrentState is a VideoView object's current state.
88 // mTargetState is the state that a method caller intends to reach.
89 // For instance, regardless the VideoView object's current state,
90 // calling pause() intends to bring the object to a target state
91 // of STATE_PAUSED.
92 private int mCurrentState = STATE_IDLE;
93 private int mTargetState = STATE_IDLE;
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 // All the stuff we need for playing and showing a video
96 private SurfaceHolder mSurfaceHolder = null;
97 private MediaPlayer mMediaPlayer = null;
Marco Nelissen13bfebd2013-05-09 15:36:53 -070098 private int mAudioSession;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private int mVideoWidth;
100 private int mVideoHeight;
101 private int mSurfaceWidth;
102 private int mSurfaceHeight;
103 private MediaController mMediaController;
104 private OnCompletionListener mOnCompletionListener;
105 private MediaPlayer.OnPreparedListener mOnPreparedListener;
106 private int mCurrentBufferPercentage;
107 private OnErrorListener mOnErrorListener;
James Donga0ba7942012-07-27 17:30:38 -0700108 private OnInfoListener mOnInfoListener;
James Dongfdf3ac62009-07-06 12:37:45 -0700109 private int mSeekWhenPrepared; // recording the seek position while preparing
Marco Nelissenc818b142009-08-19 08:32:21 -0700110 private boolean mCanPause;
111 private boolean mCanSeekBack;
112 private boolean mCanSeekForward;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113
Alan Viveretted43daf32013-09-05 16:34:30 -0700114 /** Subtitle rendering widget overlaid on top of the video. */
115 private RenderingWidget mSubtitleWidget;
Alan Viverettede213f72013-09-03 16:17:56 -0700116
Alan Viveretted43daf32013-09-05 16:34:30 -0700117 /** Listener for changes to subtitle data, used to redraw when needed. */
118 private RenderingWidget.OnChangedListener mSubtitlesChangedListener;
Alan Viverettede213f72013-09-03 16:17:56 -0700119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 public VideoView(Context context) {
121 super(context);
122 initVideoView();
123 }
Andreas Huber25643002010-01-28 11:19:57 -0800124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public VideoView(Context context, AttributeSet attrs) {
126 this(context, attrs, 0);
127 initVideoView();
128 }
Andreas Huber25643002010-01-28 11:19:57 -0800129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 public VideoView(Context context, AttributeSet attrs, int defStyle) {
131 super(context, attrs, defStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 initVideoView();
133 }
134
135 @Override
136 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Marco Nelissen0b2d8722012-11-12 15:20:57 -0800137 //Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "
138 // + MeasureSpec.toString(heightMeasureSpec) + ")");
139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
141 int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
142 if (mVideoWidth > 0 && mVideoHeight > 0) {
Marco Nelissen0b2d8722012-11-12 15:20:57 -0800143
144 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
145 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
146 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
147 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
148
149 if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {
150 // the size is fixed
151 width = widthSpecSize;
152 height = heightSpecSize;
153
154 // for compatibility, we adjust size based on aspect ratio
155 if ( mVideoWidth * height < width * mVideoHeight ) {
156 //Log.i("@@@", "image too wide, correcting");
157 width = height * mVideoWidth / mVideoHeight;
158 } else if ( mVideoWidth * height > width * mVideoHeight ) {
159 //Log.i("@@@", "image too tall, correcting");
160 height = width * mVideoHeight / mVideoWidth;
161 }
162 } else if (widthSpecMode == MeasureSpec.EXACTLY) {
163 // only the width is fixed, adjust the height to match aspect ratio if possible
164 width = widthSpecSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 height = width * mVideoHeight / mVideoWidth;
Marco Nelissen0b2d8722012-11-12 15:20:57 -0800166 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
167 // couldn't match aspect ratio within the constraints
168 height = heightSpecSize;
169 }
170 } else if (heightSpecMode == MeasureSpec.EXACTLY) {
171 // only the height is fixed, adjust the width to match aspect ratio if possible
172 height = heightSpecSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 width = height * mVideoWidth / mVideoHeight;
Marco Nelissen0b2d8722012-11-12 15:20:57 -0800174 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
175 // couldn't match aspect ratio within the constraints
176 width = widthSpecSize;
177 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 } else {
Marco Nelissen0b2d8722012-11-12 15:20:57 -0800179 // neither the width nor the height are fixed, try to use actual video size
180 width = mVideoWidth;
181 height = mVideoHeight;
182 if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {
183 // too tall, decrease both width and height
184 height = heightSpecSize;
185 width = height * mVideoWidth / mVideoHeight;
186 }
187 if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {
188 // too wide, decrease both width and height
189 width = widthSpecSize;
190 height = width * mVideoHeight / mVideoWidth;
191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
Marco Nelissen0b2d8722012-11-12 15:20:57 -0800193 } else {
194 // no size yet, just adopt the given spec sizes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 setMeasuredDimension(width, height);
197 }
Andreas Huber25643002010-01-28 11:19:57 -0800198
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800199 @Override
200 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
201 super.onInitializeAccessibilityEvent(event);
202 event.setClassName(VideoView.class.getName());
203 }
204
205 @Override
206 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
207 super.onInitializeAccessibilityNodeInfo(info);
208 info.setClassName(VideoView.class.getName());
209 }
210
Marco Nelissenc1374e82012-11-13 10:51:50 -0800211 public int resolveAdjustedSize(int desiredSize, int measureSpec) {
212 return getDefaultSize(desiredSize, measureSpec);
213 }
214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 private void initVideoView() {
216 mVideoWidth = 0;
217 mVideoHeight = 0;
218 getHolder().addCallback(mSHCallback);
219 getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
220 setFocusable(true);
221 setFocusableInTouchMode(true);
222 requestFocus();
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700223 mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();
James Dongfdf3ac62009-07-06 12:37:45 -0700224 mCurrentState = STATE_IDLE;
225 mTargetState = STATE_IDLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 }
227
228 public void setVideoPath(String path) {
229 setVideoURI(Uri.parse(path));
230 }
231
232 public void setVideoURI(Uri uri) {
Andreas Huber25643002010-01-28 11:19:57 -0800233 setVideoURI(uri, null);
234 }
235
236 /**
237 * @hide
238 */
239 public void setVideoURI(Uri uri, Map<String, String> headers) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 mUri = uri;
Andreas Huber25643002010-01-28 11:19:57 -0800241 mHeaders = headers;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 mSeekWhenPrepared = 0;
243 openVideo();
244 requestLayout();
245 invalidate();
246 }
Andreas Huber25643002010-01-28 11:19:57 -0800247
Lajos Molnaraf3098242013-08-15 20:56:53 -0700248 /**
249 * Adds an external subtitle source file (from the provided input stream.)
250 *
251 * Note that a single external subtitle source may contain multiple or no
252 * supported tracks in it. If the source contained at least one track in
253 * it, one will receive an {@link MediaPlayer#MEDIA_INFO_METADATA_UPDATE}
254 * info message. Otherwise, if reading the source takes excessive time,
255 * one will receive a {@link MediaPlayer#MEDIA_INFO_SUBTITLE_TIMED_OUT}
256 * message. If the source contained no supported track (including an empty
257 * source file or null input stream), one will receive a {@link
258 * MediaPlayer#MEDIA_INFO_UNSUPPORTED_SUBTITLE} message. One can find the
259 * total number of available tracks using {@link MediaPlayer#getTrackInfo()}
260 * to see what additional tracks become available after this method call.
261 *
262 * @param is input stream containing the subtitle data. It will be
263 * closed by the media framework.
264 * @param format the format of the subtitle track(s). Must contain at least
265 * the mime type ({@link MediaFormat#KEY_MIME}) and the
266 * language ({@link MediaFormat#KEY_LANGUAGE}) of the file.
267 * If the file itself contains the language information,
268 * specify "und" for the language.
269 */
270 public void addSubtitleSource(InputStream is, MediaFormat format) {
Lajos Molnaraf3098242013-08-15 20:56:53 -0700271 if (mMediaPlayer == null) {
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700272 mPendingSubtitleTracks.add(Pair.create(is, format));
Lajos Molnaraf3098242013-08-15 20:56:53 -0700273 } else {
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700274 try {
275 mMediaPlayer.addSubtitleSource(is, format);
276 } catch (IllegalStateException e) {
277 mInfoListener.onInfo(
278 mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
279 }
Lajos Molnaraf3098242013-08-15 20:56:53 -0700280 }
281 }
282
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700283 private Vector<Pair<InputStream, MediaFormat>> mPendingSubtitleTracks;
Lajos Molnaraf3098242013-08-15 20:56:53 -0700284
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 public void stopPlayback() {
286 if (mMediaPlayer != null) {
287 mMediaPlayer.stop();
288 mMediaPlayer.release();
289 mMediaPlayer = null;
James Dongfdf3ac62009-07-06 12:37:45 -0700290 mCurrentState = STATE_IDLE;
291 mTargetState = STATE_IDLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 }
293 }
294
295 private void openVideo() {
296 if (mUri == null || mSurfaceHolder == null) {
297 // not ready for playback just yet, will try again later
298 return;
299 }
Andreas Huber25643002010-01-28 11:19:57 -0800300 // Tell the music playback service to pause
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 // TODO: these constants need to be published somewhere in the framework.
302 Intent i = new Intent("com.android.music.musicservicecommand");
303 i.putExtra("command", "pause");
304 mContext.sendBroadcast(i);
James Dongfdf3ac62009-07-06 12:37:45 -0700305
Marco Nelissendddeee62009-07-10 14:14:52 -0700306 // we shouldn't clear the target state, because somebody might have
307 // called start() previously
308 release(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 try {
310 mMediaPlayer = new MediaPlayer();
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700311 // TODO: create SubtitleController in MediaPlayer, but we need
312 // a context for the subtitle renderers
Alan Viveretted43daf32013-09-05 16:34:30 -0700313 final Context context = getContext();
314 final SubtitleController controller = new SubtitleController(
315 context, mMediaPlayer.getMediaTimeProvider(), mMediaPlayer);
316 controller.registerRenderer(new WebVttRenderer(context));
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700317 mMediaPlayer.setSubtitleAnchor(controller, this);
318
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700319 if (mAudioSession != 0) {
320 mMediaPlayer.setAudioSessionId(mAudioSession);
321 } else {
322 mAudioSession = mMediaPlayer.getAudioSessionId();
323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 mMediaPlayer.setOnPreparedListener(mPreparedListener);
325 mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 mMediaPlayer.setOnCompletionListener(mCompletionListener);
327 mMediaPlayer.setOnErrorListener(mErrorListener);
Lajos Molnaraf3098242013-08-15 20:56:53 -0700328 mMediaPlayer.setOnInfoListener(mInfoListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);
330 mCurrentBufferPercentage = 0;
Andreas Huber25643002010-01-28 11:19:57 -0800331 mMediaPlayer.setDataSource(mContext, mUri, mHeaders);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 mMediaPlayer.setDisplay(mSurfaceHolder);
333 mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
334 mMediaPlayer.setScreenOnWhilePlaying(true);
335 mMediaPlayer.prepareAsync();
Lajos Molnaraf3098242013-08-15 20:56:53 -0700336
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700337 for (Pair<InputStream, MediaFormat> pending: mPendingSubtitleTracks) {
338 try {
339 mMediaPlayer.addSubtitleSource(pending.first, pending.second);
340 } catch (IllegalStateException e) {
341 mInfoListener.onInfo(
342 mMediaPlayer, MediaPlayer.MEDIA_INFO_UNSUPPORTED_SUBTITLE, 0);
343 }
Lajos Molnaraf3098242013-08-15 20:56:53 -0700344 }
345
Marco Nelissendddeee62009-07-10 14:14:52 -0700346 // we don't set the target state here either, but preserve the
347 // target state that was there before.
James Dongfdf3ac62009-07-06 12:37:45 -0700348 mCurrentState = STATE_PREPARING;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 attachMediaController();
350 } catch (IOException ex) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700351 Log.w(TAG, "Unable to open content: " + mUri, ex);
James Dongfdf3ac62009-07-06 12:37:45 -0700352 mCurrentState = STATE_ERROR;
353 mTargetState = STATE_ERROR;
Andrei Popescu020d2e32009-09-30 14:54:55 +0100354 mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 return;
356 } catch (IllegalArgumentException ex) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700357 Log.w(TAG, "Unable to open content: " + mUri, ex);
James Dongfdf3ac62009-07-06 12:37:45 -0700358 mCurrentState = STATE_ERROR;
359 mTargetState = STATE_ERROR;
Andrei Popescu020d2e32009-09-30 14:54:55 +0100360 mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 return;
Lajos Molnaraf3098242013-08-15 20:56:53 -0700362 } finally {
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700363 mPendingSubtitleTracks.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365 }
Andreas Huber25643002010-01-28 11:19:57 -0800366
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 public void setMediaController(MediaController controller) {
368 if (mMediaController != null) {
369 mMediaController.hide();
370 }
371 mMediaController = controller;
372 attachMediaController();
373 }
374
375 private void attachMediaController() {
376 if (mMediaPlayer != null && mMediaController != null) {
377 mMediaController.setMediaPlayer(this);
378 View anchorView = this.getParent() instanceof View ?
379 (View)this.getParent() : this;
380 mMediaController.setAnchorView(anchorView);
James Dongfdf3ac62009-07-06 12:37:45 -0700381 mMediaController.setEnabled(isInPlaybackState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 }
383 }
Andreas Huber25643002010-01-28 11:19:57 -0800384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =
386 new MediaPlayer.OnVideoSizeChangedListener() {
387 public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
388 mVideoWidth = mp.getVideoWidth();
389 mVideoHeight = mp.getVideoHeight();
390 if (mVideoWidth != 0 && mVideoHeight != 0) {
391 getHolder().setFixedSize(mVideoWidth, mVideoHeight);
Teng-Hui Zhuf5d102e2012-08-23 15:24:29 -0700392 requestLayout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 }
394 }
395 };
Andreas Huber25643002010-01-28 11:19:57 -0800396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
398 public void onPrepared(MediaPlayer mp) {
James Dongfdf3ac62009-07-06 12:37:45 -0700399 mCurrentState = STATE_PREPARED;
Marco Nelissenc818b142009-08-19 08:32:21 -0700400
401 // Get the capabilities of the player for this stream
402 Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
403 MediaPlayer.BYPASS_METADATA_FILTER);
Andreas Huberd44d33b2009-08-20 11:12:27 -0700404
405 if (data != null) {
406 mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
407 || data.getBoolean(Metadata.PAUSE_AVAILABLE);
408 mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
409 || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
410 mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
411 || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
412 } else {
Andreas Huber2869e952010-03-09 09:18:01 -0800413 mCanPause = mCanSeekBack = mCanSeekForward = true;
Andreas Huberd44d33b2009-08-20 11:12:27 -0700414 }
Marco Nelissenc818b142009-08-19 08:32:21 -0700415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 if (mOnPreparedListener != null) {
417 mOnPreparedListener.onPrepared(mMediaPlayer);
418 }
419 if (mMediaController != null) {
420 mMediaController.setEnabled(true);
421 }
422 mVideoWidth = mp.getVideoWidth();
423 mVideoHeight = mp.getVideoHeight();
Marco Nelissenc818b142009-08-19 08:32:21 -0700424
James Dongfdf3ac62009-07-06 12:37:45 -0700425 int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
426 if (seekToPosition != 0) {
427 seekTo(seekToPosition);
428 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 if (mVideoWidth != 0 && mVideoHeight != 0) {
430 //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
431 getHolder().setFixedSize(mVideoWidth, mVideoHeight);
432 if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {
433 // We didn't actually change the size (it was already at the size
434 // we need), so we won't get a "surface changed" callback, so
435 // start the video here instead of in the callback.
James Dongfdf3ac62009-07-06 12:37:45 -0700436 if (mTargetState == STATE_PLAYING) {
James Dongaa00e392009-07-02 16:26:39 -0700437 start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 if (mMediaController != null) {
439 mMediaController.show();
440 }
441 } else if (!isPlaying() &&
James Dongfdf3ac62009-07-06 12:37:45 -0700442 (seekToPosition != 0 || getCurrentPosition() > 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 if (mMediaController != null) {
444 // Show the media controls when we're paused into a video and make 'em stick.
445 mMediaController.show(0);
446 }
447 }
448 }
449 } else {
450 // We don't know the video size yet, but should start anyway.
451 // The video size might be reported to us later.
James Dongfdf3ac62009-07-06 12:37:45 -0700452 if (mTargetState == STATE_PLAYING) {
James Dongaa00e392009-07-02 16:26:39 -0700453 start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 }
455 }
456 }
457 };
458
459 private MediaPlayer.OnCompletionListener mCompletionListener =
460 new MediaPlayer.OnCompletionListener() {
461 public void onCompletion(MediaPlayer mp) {
James Dongfdf3ac62009-07-06 12:37:45 -0700462 mCurrentState = STATE_PLAYBACK_COMPLETED;
463 mTargetState = STATE_PLAYBACK_COMPLETED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 if (mMediaController != null) {
465 mMediaController.hide();
466 }
467 if (mOnCompletionListener != null) {
468 mOnCompletionListener.onCompletion(mMediaPlayer);
469 }
470 }
471 };
472
Lajos Molnaraf3098242013-08-15 20:56:53 -0700473 private MediaPlayer.OnInfoListener mInfoListener =
474 new MediaPlayer.OnInfoListener() {
475 public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {
476 if (mOnInfoListener != null) {
477 mOnInfoListener.onInfo(mp, arg1, arg2);
478 }
479 return true;
480 }
481 };
482
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 private MediaPlayer.OnErrorListener mErrorListener =
484 new MediaPlayer.OnErrorListener() {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700485 public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
486 Log.d(TAG, "Error: " + framework_err + "," + impl_err);
James Dongfdf3ac62009-07-06 12:37:45 -0700487 mCurrentState = STATE_ERROR;
488 mTargetState = STATE_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 if (mMediaController != null) {
490 mMediaController.hide();
491 }
492
493 /* If an error handler has been supplied, use it and finish. */
494 if (mOnErrorListener != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700495 if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 return true;
497 }
498 }
499
500 /* Otherwise, pop up an error dialog so the user knows that
501 * something bad has happened. Only try and pop up the dialog
502 * if we're attached to a window. When we're going away and no
503 * longer have a window, don't bother showing the user an error.
504 */
505 if (getWindowToken() != null) {
506 Resources r = mContext.getResources();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700507 int messageId;
508
509 if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
510 messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;
511 } else {
512 messageId = com.android.internal.R.string.VideoView_error_text_unknown;
513 }
514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 new AlertDialog.Builder(mContext)
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700516 .setMessage(messageId)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 .setPositiveButton(com.android.internal.R.string.VideoView_error_button,
518 new DialogInterface.OnClickListener() {
519 public void onClick(DialogInterface dialog, int whichButton) {
520 /* If we get here, there is no onError listener, so
521 * at least inform them that the video is over.
522 */
523 if (mOnCompletionListener != null) {
524 mOnCompletionListener.onCompletion(mMediaPlayer);
525 }
526 }
527 })
528 .setCancelable(false)
529 .show();
530 }
531 return true;
532 }
533 };
534
535 private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =
536 new MediaPlayer.OnBufferingUpdateListener() {
537 public void onBufferingUpdate(MediaPlayer mp, int percent) {
538 mCurrentBufferPercentage = percent;
539 }
540 };
541
542 /**
543 * Register a callback to be invoked when the media file
544 * is loaded and ready to go.
545 *
546 * @param l The callback that will be run
547 */
548 public void setOnPreparedListener(MediaPlayer.OnPreparedListener l)
549 {
550 mOnPreparedListener = l;
551 }
552
553 /**
554 * Register a callback to be invoked when the end of a media file
555 * has been reached during playback.
556 *
557 * @param l The callback that will be run
558 */
559 public void setOnCompletionListener(OnCompletionListener l)
560 {
561 mOnCompletionListener = l;
562 }
563
564 /**
565 * Register a callback to be invoked when an error occurs
566 * during playback or setup. If no listener is specified,
567 * or if the listener returned false, VideoView will inform
568 * the user of any errors.
569 *
570 * @param l The callback that will be run
571 */
572 public void setOnErrorListener(OnErrorListener l)
573 {
574 mOnErrorListener = l;
575 }
576
James Donga0ba7942012-07-27 17:30:38 -0700577 /**
578 * Register a callback to be invoked when an informational event
579 * occurs during playback or setup.
580 *
581 * @param l The callback that will be run
582 */
583 public void setOnInfoListener(OnInfoListener l) {
584 mOnInfoListener = l;
585 }
586
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()
588 {
589 public void surfaceChanged(SurfaceHolder holder, int format,
590 int w, int h)
591 {
592 mSurfaceWidth = w;
593 mSurfaceHeight = h;
James Dongfdf3ac62009-07-06 12:37:45 -0700594 boolean isValidState = (mTargetState == STATE_PLAYING);
595 boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);
596 if (mMediaPlayer != null && isValidState && hasValidSize) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 if (mSeekWhenPrepared != 0) {
James Dongfdf3ac62009-07-06 12:37:45 -0700598 seekTo(mSeekWhenPrepared);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
James Dongfdf3ac62009-07-06 12:37:45 -0700600 start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 }
602 }
603
604 public void surfaceCreated(SurfaceHolder holder)
605 {
606 mSurfaceHolder = holder;
Andreas Huber69b8d692010-10-29 12:00:20 -0700607 openVideo();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
609
610 public void surfaceDestroyed(SurfaceHolder holder)
611 {
612 // after we return from this we can't use the surface any more
613 mSurfaceHolder = null;
614 if (mMediaController != null) mMediaController.hide();
Andreas Huber69b8d692010-10-29 12:00:20 -0700615 release(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 }
617 };
618
James Dongfdf3ac62009-07-06 12:37:45 -0700619 /*
620 * release the media player in any state
621 */
Marco Nelissendddeee62009-07-10 14:14:52 -0700622 private void release(boolean cleartargetstate) {
James Dongfdf3ac62009-07-06 12:37:45 -0700623 if (mMediaPlayer != null) {
624 mMediaPlayer.reset();
625 mMediaPlayer.release();
626 mMediaPlayer = null;
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700627 mPendingSubtitleTracks.clear();
James Dongfdf3ac62009-07-06 12:37:45 -0700628 mCurrentState = STATE_IDLE;
Marco Nelissendddeee62009-07-10 14:14:52 -0700629 if (cleartargetstate) {
630 mTargetState = STATE_IDLE;
631 }
James Dongfdf3ac62009-07-06 12:37:45 -0700632 }
633 }
634
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 @Override
636 public boolean onTouchEvent(MotionEvent ev) {
James Dongfdf3ac62009-07-06 12:37:45 -0700637 if (isInPlaybackState() && mMediaController != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 toggleMediaControlsVisiblity();
639 }
640 return false;
641 }
Andreas Huber25643002010-01-28 11:19:57 -0800642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 @Override
644 public boolean onTrackballEvent(MotionEvent ev) {
James Dongfdf3ac62009-07-06 12:37:45 -0700645 if (isInPlaybackState() && mMediaController != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 toggleMediaControlsVisiblity();
647 }
648 return false;
649 }
Andreas Huber25643002010-01-28 11:19:57 -0800650
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 @Override
652 public boolean onKeyDown(int keyCode, KeyEvent event)
653 {
James Dongfdf3ac62009-07-06 12:37:45 -0700654 boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
655 keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
656 keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
Jeff Brownb0418da2010-11-01 15:24:01 -0700657 keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
James Dongfdf3ac62009-07-06 12:37:45 -0700658 keyCode != KeyEvent.KEYCODE_MENU &&
659 keyCode != KeyEvent.KEYCODE_CALL &&
660 keyCode != KeyEvent.KEYCODE_ENDCALL;
661 if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
Andy Stadlerf8a7cea2009-04-10 16:24:47 -0700663 keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 if (mMediaPlayer.isPlaying()) {
665 pause();
666 mMediaController.show();
667 } else {
668 start();
669 mMediaController.hide();
670 }
671 return true;
Jeff Brown4d396052010-10-29 21:50:21 -0700672 } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
Chih-Chung Changd7db7012011-02-10 18:11:27 +0800673 if (!mMediaPlayer.isPlaying()) {
Jeff Brown4d396052010-10-29 21:50:21 -0700674 start();
675 mMediaController.hide();
676 }
677 return true;
Andreas Huber25643002010-01-28 11:19:57 -0800678 } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
Jeff Brown4d396052010-10-29 21:50:21 -0700679 || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
Chih-Chung Changd7db7012011-02-10 18:11:27 +0800680 if (mMediaPlayer.isPlaying()) {
Jeff Brown4d396052010-10-29 21:50:21 -0700681 pause();
682 mMediaController.show();
683 }
684 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 } else {
686 toggleMediaControlsVisiblity();
687 }
688 }
689
690 return super.onKeyDown(keyCode, event);
691 }
692
693 private void toggleMediaControlsVisiblity() {
Andreas Huber25643002010-01-28 11:19:57 -0800694 if (mMediaController.isShowing()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 mMediaController.hide();
696 } else {
697 mMediaController.show();
698 }
699 }
Andreas Huber25643002010-01-28 11:19:57 -0800700
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700701 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 public void start() {
James Dongfdf3ac62009-07-06 12:37:45 -0700703 if (isInPlaybackState()) {
704 mMediaPlayer.start();
705 mCurrentState = STATE_PLAYING;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 }
James Dongfdf3ac62009-07-06 12:37:45 -0700707 mTargetState = STATE_PLAYING;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 }
Andreas Huber25643002010-01-28 11:19:57 -0800709
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700710 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800711 public void pause() {
James Dongfdf3ac62009-07-06 12:37:45 -0700712 if (isInPlaybackState()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 if (mMediaPlayer.isPlaying()) {
714 mMediaPlayer.pause();
James Dongfdf3ac62009-07-06 12:37:45 -0700715 mCurrentState = STATE_PAUSED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
717 }
James Dongfdf3ac62009-07-06 12:37:45 -0700718 mTargetState = STATE_PAUSED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 }
Andreas Huber25643002010-01-28 11:19:57 -0800720
Gloria Wang2e1818a2010-02-22 14:49:13 -0800721 public void suspend() {
Andreas Huber69b8d692010-10-29 12:00:20 -0700722 release(false);
Gloria Wang2e1818a2010-02-22 14:49:13 -0800723 }
724
725 public void resume() {
Andreas Huber69b8d692010-10-29 12:00:20 -0700726 openVideo();
Gloria Wang2e1818a2010-02-22 14:49:13 -0800727 }
728
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700729 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 public int getDuration() {
James Dongfdf3ac62009-07-06 12:37:45 -0700731 if (isInPlaybackState()) {
Andreas Huber585c07e2012-11-27 15:06:56 -0800732 return mMediaPlayer.getDuration();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 }
Andreas Huber585c07e2012-11-27 15:06:56 -0800734
735 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
Andreas Huber25643002010-01-28 11:19:57 -0800737
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700738 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 public int getCurrentPosition() {
James Dongfdf3ac62009-07-06 12:37:45 -0700740 if (isInPlaybackState()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 return mMediaPlayer.getCurrentPosition();
742 }
743 return 0;
744 }
Andreas Huber25643002010-01-28 11:19:57 -0800745
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700746 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 public void seekTo(int msec) {
James Dongfdf3ac62009-07-06 12:37:45 -0700748 if (isInPlaybackState()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 mMediaPlayer.seekTo(msec);
James Dongfdf3ac62009-07-06 12:37:45 -0700750 mSeekWhenPrepared = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 } else {
752 mSeekWhenPrepared = msec;
753 }
Andreas Huber25643002010-01-28 11:19:57 -0800754 }
755
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700756 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 public boolean isPlaying() {
James Dongfdf3ac62009-07-06 12:37:45 -0700758 return isInPlaybackState() && mMediaPlayer.isPlaying();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
Andreas Huber25643002010-01-28 11:19:57 -0800760
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700761 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 public int getBufferPercentage() {
763 if (mMediaPlayer != null) {
764 return mCurrentBufferPercentage;
765 }
766 return 0;
767 }
James Dongfdf3ac62009-07-06 12:37:45 -0700768
769 private boolean isInPlaybackState() {
770 return (mMediaPlayer != null &&
771 mCurrentState != STATE_ERROR &&
772 mCurrentState != STATE_IDLE &&
773 mCurrentState != STATE_PREPARING);
774 }
Marco Nelissenc818b142009-08-19 08:32:21 -0700775
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700776 @Override
Marco Nelissenc818b142009-08-19 08:32:21 -0700777 public boolean canPause() {
778 return mCanPause;
779 }
780
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700781 @Override
Marco Nelissenc818b142009-08-19 08:32:21 -0700782 public boolean canSeekBackward() {
783 return mCanSeekBack;
784 }
785
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700786 @Override
Marco Nelissenc818b142009-08-19 08:32:21 -0700787 public boolean canSeekForward() {
788 return mCanSeekForward;
789 }
Marco Nelissen13bfebd2013-05-09 15:36:53 -0700790
791 @Override
792 public int getAudioSessionId() {
793 if (mAudioSession == 0) {
794 MediaPlayer foo = new MediaPlayer();
795 mAudioSession = foo.getAudioSessionId();
796 foo.release();
797 }
798 return mAudioSession;
799 }
Alan Viverettede213f72013-09-03 16:17:56 -0700800
801 @Override
Alan Viveretted43daf32013-09-05 16:34:30 -0700802 protected void onAttachedToWindow() {
803 super.onAttachedToWindow();
804
805 if (mSubtitleWidget != null) {
806 mSubtitleWidget.onAttachedToWindow();
807 }
808 }
809
810 @Override
811 protected void onDetachedFromWindow() {
812 super.onDetachedFromWindow();
813
814 if (mSubtitleWidget != null) {
815 mSubtitleWidget.onDetachedFromWindow();
816 }
817 }
818
819 @Override
Alan Viverettede213f72013-09-03 16:17:56 -0700820 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
821 super.onLayout(changed, left, top, right, bottom);
822
Alan Viveretted43daf32013-09-05 16:34:30 -0700823 if (mSubtitleWidget != null) {
824 measureAndLayoutSubtitleWidget();
Alan Viverettede213f72013-09-03 16:17:56 -0700825 }
826 }
827
828 @Override
829 public void draw(Canvas canvas) {
830 super.draw(canvas);
831
Alan Viveretted43daf32013-09-05 16:34:30 -0700832 if (mSubtitleWidget != null) {
833 final int saveCount = canvas.save();
834 canvas.translate(getPaddingLeft(), getPaddingTop());
835 mSubtitleWidget.draw(canvas);
836 canvas.restoreToCount(saveCount);
Alan Viverettede213f72013-09-03 16:17:56 -0700837 }
838 }
839
840 /**
Alan Viverettede213f72013-09-03 16:17:56 -0700841 * Forces a measurement and layout pass for all overlaid views.
842 *
Alan Viveretted43daf32013-09-05 16:34:30 -0700843 * @see #setSubtitleWidget(RenderingWidget)
Alan Viverettede213f72013-09-03 16:17:56 -0700844 */
Alan Viveretted43daf32013-09-05 16:34:30 -0700845 private void measureAndLayoutSubtitleWidget() {
846 final int width = getWidth() - getPaddingLeft() - getPaddingRight();
847 final int height = getHeight() - getPaddingTop() - getPaddingBottom();
Alan Viverettede213f72013-09-03 16:17:56 -0700848
Alan Viveretted43daf32013-09-05 16:34:30 -0700849 mSubtitleWidget.setSize(width, height);
Alan Viverettede213f72013-09-03 16:17:56 -0700850 }
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700851
852 /** @hide */
853 @Override
Alan Viveretted43daf32013-09-05 16:34:30 -0700854 public void setSubtitleWidget(RenderingWidget subtitleWidget) {
855 if (mSubtitleWidget == subtitleWidget) {
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700856 return;
857 }
858
Alan Viveretted43daf32013-09-05 16:34:30 -0700859 final boolean attachedToWindow = isAttachedToWindow();
860 if (mSubtitleWidget != null) {
861 if (attachedToWindow) {
862 mSubtitleWidget.onDetachedFromWindow();
863 }
Lajos Molnar484ff7a92013-08-15 11:37:47 -0700864
Alan Viveretted43daf32013-09-05 16:34:30 -0700865 mSubtitleWidget.setOnChangedListener(null);
866 }
867
868 mSubtitleWidget = subtitleWidget;
869
870 if (subtitleWidget != null) {
871 if (mSubtitlesChangedListener == null) {
872 mSubtitlesChangedListener = new RenderingWidget.OnChangedListener() {
873 @Override
874 public void onChanged(RenderingWidget renderingWidget) {
875 invalidate();
876 }
877 };
878 }
879
880 setWillNotDraw(false);
881 subtitleWidget.setOnChangedListener(mSubtitlesChangedListener);
882
883 if (attachedToWindow) {
884 subtitleWidget.onAttachedToWindow();
885 requestLayout();
886 }
887 } else {
888 setWillNotDraw(true);
889 }
890
891 invalidate();
892 }
Lajos Molnar29f51832013-09-20 08:45:31 -0700893
894 /** @hide */
895 @Override
896 public Looper getSubtitleLooper() {
897 return Looper.getMainLooper();
898 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899}