VideoView2.java revision 6bd3ab6dc6ad4001c387d7f4a4a2ce954be6ca16
1/* 2 * Copyright 2018 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.annotation.IntDef; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.content.Context; 23import android.media.AudioAttributes; 24import android.media.AudioManager; 25import android.media.DataSourceDesc; 26import android.media.MediaItem2; 27import android.media.MediaMetadata2; 28import android.media.MediaPlayer2; 29import android.media.SessionToken2; 30import android.media.session.MediaController; 31import android.media.session.PlaybackState; 32import android.media.update.ApiLoader; 33import android.media.update.VideoView2Provider; 34import android.media.update.ViewGroupHelper; 35import android.net.Uri; 36import android.os.Bundle; 37import android.util.AttributeSet; 38import android.view.View; 39 40import com.android.internal.annotations.VisibleForTesting; 41 42import java.lang.annotation.Retention; 43import java.lang.annotation.RetentionPolicy; 44import java.util.List; 45import java.util.Map; 46import java.util.concurrent.Executor; 47 48// TODO: Replace MediaSession wtih MediaSession2 once MediaSession2 is submitted. 49/** 50 * Displays a video file. VideoView2 class is a View class which is wrapping {@link MediaPlayer2} 51 * so that developers can easily implement a video rendering application. 52 * 53 * <p> 54 * <em> Data sources that VideoView2 supports : </em> 55 * VideoView2 can play video files and audio-only files as 56 * well. It can load from various sources such as resources or content providers. The supported 57 * media file formats are the same as {@link MediaPlayer2}. 58 * 59 * <p> 60 * <em> View type can be selected : </em> 61 * VideoView2 can render videos on top of TextureView as well as 62 * SurfaceView selectively. The default is SurfaceView and it can be changed using 63 * {@link #setViewType(int)} method. Using SurfaceView is recommended in most cases for saving 64 * battery. TextureView might be preferred for supporting various UIs such as animation and 65 * translucency. 66 * 67 * <p> 68 * <em> Differences between {@link VideoView} class : </em> 69 * VideoView2 covers and inherits the most of 70 * VideoView's functionalities. The main differences are 71 * <ul> 72 * <li> VideoView2 inherits FrameLayout and renders videos using SurfaceView and TextureView 73 * selectively while VideoView inherits SurfaceView class. 74 * <li> VideoView2 is integrated with MediaControlView2 and a default MediaControlView2 instance is 75 * attached to VideoView2 by default. If a developer does not want to use the default 76 * MediaControlView2, needs to set enableControlView attribute to false. For instance, 77 * <pre> 78 * <VideoView2 79 * android:id="@+id/video_view" 80 * xmlns:widget="http://schemas.android.com/apk/com.android.media.update" 81 * widget:enableControlView="false" /> 82 * </pre> 83 * If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute 84 * to false and assign the customed media control widget using {@link #setMediaControlView2}. 85 * <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer. 86 * <li> VideoView2 is integrated with MediaSession and so it responses with media key events. 87 * A VideoView2 keeps a MediaSession instance internally and connects it to a corresponding 88 * MediaControlView2 instance. 89 * </p> 90 * </ul> 91 * 92 * <p> 93 * <em> Audio focus and audio attributes : </em> 94 * By default, VideoView2 requests audio focus with 95 * {@link AudioManager#AUDIOFOCUS_GAIN}. Use {@link #setAudioFocusRequest(int)} to change this 96 * behavior. The default {@link AudioAttributes} used during playback have a usage of 97 * {@link AudioAttributes#USAGE_MEDIA} and a content type of 98 * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, use {@link #setAudioAttributes(AudioAttributes)} to 99 * modify them. 100 * 101 * <p> 102 * Note: VideoView2 does not retain its full state when going into the background. In particular, it 103 * does not restore the current play state, play position, selected tracks. Applications should save 104 * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and 105 * {@link android.app.Activity#onRestoreInstanceState}. 106 */ 107public class VideoView2 extends ViewGroupHelper<VideoView2Provider> { 108 /** @hide */ 109 @IntDef({ 110 VIEW_TYPE_TEXTUREVIEW, 111 VIEW_TYPE_SURFACEVIEW 112 }) 113 @Retention(RetentionPolicy.SOURCE) 114 public @interface ViewType {} 115 116 /** 117 * Indicates video is rendering on SurfaceView. 118 * 119 * @see #setViewType 120 */ 121 public static final int VIEW_TYPE_SURFACEVIEW = 1; 122 123 /** 124 * Indicates video is rendering on TextureView. 125 * 126 * @see #setViewType 127 */ 128 public static final int VIEW_TYPE_TEXTUREVIEW = 2; 129 130 public VideoView2(@NonNull Context context) { 131 this(context, null); 132 } 133 134 public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs) { 135 this(context, attrs, 0); 136 } 137 138 public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 139 this(context, attrs, defStyleAttr, 0); 140 } 141 142 public VideoView2( 143 @NonNull Context context, @Nullable AttributeSet attrs, 144 int defStyleAttr, int defStyleRes) { 145 super((instance, superProvider, privateProvider) -> 146 ApiLoader.getProvider().createVideoView2( 147 (VideoView2) instance, superProvider, privateProvider, 148 attrs, defStyleAttr, defStyleRes), 149 context, attrs, defStyleAttr, defStyleRes); 150 mProvider.initialize(attrs, defStyleAttr, defStyleRes); 151 } 152 153 /** 154 * Sets MediaControlView2 instance. It will replace the previously assigned MediaControlView2 155 * instance if any. 156 * 157 * @param mediaControlView a media control view2 instance. 158 * @param intervalMs a time interval in milliseconds until VideoView2 hides MediaControlView2. 159 */ 160 public void setMediaControlView2(MediaControlView2 mediaControlView, long intervalMs) { 161 mProvider.setMediaControlView2_impl(mediaControlView, intervalMs); 162 } 163 164 /** 165 * Returns MediaControlView2 instance which is currently attached to VideoView2 by default or by 166 * {@link #setMediaControlView2} method. 167 */ 168 public MediaControlView2 getMediaControlView2() { 169 return mProvider.getMediaControlView2_impl(); 170 } 171 172 /** 173 * Sets MediaMetadata2 instance. It will replace the previously assigned MediaMetadata2 instance 174 * if any. 175 * 176 * @param metadata a MediaMetadata2 instance. 177 * @hide 178 */ 179 public void setMediaMetadata(MediaMetadata2 metadata) { 180 mProvider.setMediaMetadata_impl(metadata); 181 } 182 183 /** 184 * Returns MediaMetadata2 instance which is retrieved from MediaPlayer2 inside VideoView2 by 185 * default or by {@link #setMediaMetadata} method. 186 * @hide 187 */ 188 public MediaMetadata2 getMediaMetadata() { 189 // TODO: add to Javadoc whether this value can be null or not when integrating with 190 // MediaSession2. 191 return mProvider.getMediaMetadata_impl(); 192 } 193 194 /** 195 * Returns MediaController instance which is connected with MediaSession that VideoView2 is 196 * using. This method should be called when VideoView2 is attached to window, or it throws 197 * IllegalStateException, since internal MediaSession instance is not available until 198 * this view is attached to window. Please check {@link android.view.View#isAttachedToWindow} 199 * before calling this method. 200 * 201 * @throws IllegalStateException if interal MediaSession is not created yet. 202 * @hide TODO: remove 203 */ 204 public MediaController getMediaController() { 205 return mProvider.getMediaController_impl(); 206 } 207 208 /** 209 * Returns {@link android.media.SessionToken2} so that developers create their own 210 * {@link android.media.MediaController2} instance. This method should be called when VideoView2 211 * is attached to window, or it throws IllegalStateException. 212 * 213 * @throws IllegalStateException if interal MediaSession is not created yet. 214 */ 215 public SessionToken2 getMediaSessionToken() { 216 return mProvider.getMediaSessionToken_impl(); 217 } 218 219 /** 220 * Shows or hides closed caption or subtitles if there is any. 221 * The first subtitle track will be chosen if there multiple subtitle tracks exist. 222 * Default behavior of VideoView2 is not showing subtitle. 223 * @param enable shows closed caption or subtitles if this value is true, or hides. 224 */ 225 public void setSubtitleEnabled(boolean enable) { 226 mProvider.setSubtitleEnabled_impl(enable); 227 } 228 229 /** 230 * Returns true if showing subtitle feature is enabled or returns false. 231 * Although there is no subtitle track or closed caption, it can return true, if the feature 232 * has been enabled by {@link #setSubtitleEnabled}. 233 */ 234 public boolean isSubtitleEnabled() { 235 return mProvider.isSubtitleEnabled_impl(); 236 } 237 238 /** 239 * Sets playback speed. 240 * 241 * It is expressed as a multiplicative factor, where normal speed is 1.0f. If it is less than 242 * or equal to zero, it will be just ignored and nothing will be changed. If it exceeds the 243 * maximum speed that internal engine supports, system will determine best handling or it will 244 * be reset to the normal speed 1.0f. 245 * @param speed the playback speed. It should be positive. 246 */ 247 // TODO: Support this via MediaController2. 248 public void setSpeed(float speed) { 249 mProvider.setSpeed_impl(speed); 250 } 251 252 /** 253 * Sets which type of audio focus will be requested during the playback, or configures playback 254 * to not request audio focus. Valid values for focus requests are 255 * {@link AudioManager#AUDIOFOCUS_GAIN}, {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT}, 256 * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}, and 257 * {@link AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}. Or use 258 * {@link AudioManager#AUDIOFOCUS_NONE} to express that audio focus should not be 259 * requested when playback starts. You can for instance use this when playing a silent animation 260 * through this class, and you don't want to affect other audio applications playing in the 261 * background. 262 * 263 * @param focusGain the type of audio focus gain that will be requested, or 264 * {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during 265 * playback. 266 */ 267 public void setAudioFocusRequest(int focusGain) { 268 mProvider.setAudioFocusRequest_impl(focusGain); 269 } 270 271 /** 272 * Sets the {@link AudioAttributes} to be used during the playback of the video. 273 * 274 * @param attributes non-null <code>AudioAttributes</code>. 275 */ 276 public void setAudioAttributes(@NonNull AudioAttributes attributes) { 277 mProvider.setAudioAttributes_impl(attributes); 278 } 279 280 /** 281 * Sets video path. 282 * 283 * @param path the path of the video. 284 * 285 * @hide TODO remove 286 */ 287 public void setVideoPath(String path) { 288 mProvider.setVideoPath_impl(path); 289 } 290 291 /** 292 * Sets video URI. 293 * 294 * @param uri the URI of the video. 295 * 296 * @hide TODO remove 297 */ 298 public void setVideoUri(Uri uri) { 299 mProvider.setVideoUri_impl(uri); 300 } 301 302 /** 303 * Sets video URI using specific headers. 304 * 305 * @param uri the URI of the video. 306 * @param headers the headers for the URI request. 307 * Note that the cross domain redirection is allowed by default, but that can be 308 * changed with key/value pairs through the headers parameter with 309 * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value 310 * to disallow or allow cross domain redirection. 311 * 312 * @hide TODO remove 313 */ 314 public void setVideoUri(Uri uri, Map<String, String> headers) { 315 mProvider.setVideoUri_impl(uri, headers); 316 } 317 318 /** 319 * Sets {@link MediaItem2} object to render using VideoView2. Alternative way to set media 320 * object to VideoView2 is {@link #setDataSource}. 321 * @param mediaItem the MediaItem2 to play 322 * @see #setDataSource 323 */ 324 public void setMediaItem(@NonNull MediaItem2 mediaItem) { 325 mProvider.setMediaItem_impl(mediaItem); 326 } 327 328 /** 329 * Sets {@link DataSourceDesc} object to render using VideoView2. 330 * @param dataSource the {@link DataSourceDesc} object to play. 331 * @see #setMediaItem 332 */ 333 public void setDataSource(@NonNull DataSourceDesc dataSource) { 334 mProvider.setDataSource_impl(dataSource); 335 } 336 337 /** 338 * Selects which view will be used to render video between SurfacView and TextureView. 339 * 340 * @param viewType the view type to render video 341 * <ul> 342 * <li>{@link #VIEW_TYPE_SURFACEVIEW} 343 * <li>{@link #VIEW_TYPE_TEXTUREVIEW} 344 * </ul> 345 */ 346 public void setViewType(@ViewType int viewType) { 347 mProvider.setViewType_impl(viewType); 348 } 349 350 /** 351 * Returns view type. 352 * 353 * @return view type. See {@see setViewType}. 354 */ 355 @ViewType 356 public int getViewType() { 357 return mProvider.getViewType_impl(); 358 } 359 360 /** 361 * Sets custom actions which will be shown as custom buttons in {@link MediaControlView2}. 362 * 363 * @param actionList A list of {@link PlaybackState.CustomAction}. The return value of 364 * {@link PlaybackState.CustomAction#getIcon()} will be used to draw buttons 365 * in {@link MediaControlView2}. 366 * @param executor executor to run callbacks on. 367 * @param listener A listener to be called when a custom button is clicked. 368 * @hide TODO remove 369 */ 370 public void setCustomActions(List<PlaybackState.CustomAction> actionList, 371 Executor executor, OnCustomActionListener listener) { 372 mProvider.setCustomActions_impl(actionList, executor, listener); 373 } 374 375 /** 376 * Registers a callback to be invoked when a view type change is done. 377 * {@see #setViewType(int)} 378 * @param l The callback that will be run 379 * @hide 380 */ 381 @VisibleForTesting 382 public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) { 383 mProvider.setOnViewTypeChangedListener_impl(l); 384 } 385 386 /** 387 * Registers a callback to be invoked when the fullscreen mode should be changed. 388 * @param l The callback that will be run 389 * @hide TODO remove 390 */ 391 public void setFullScreenRequestListener(OnFullScreenRequestListener l) { 392 mProvider.setFullScreenRequestListener_impl(l); 393 } 394 395 /** 396 * Interface definition of a callback to be invoked when the view type has been changed. 397 * 398 * @hide 399 */ 400 @VisibleForTesting 401 public interface OnViewTypeChangedListener { 402 /** 403 * Called when the view type has been changed. 404 * @see #setViewType(int) 405 * @param view the View whose view type is changed 406 * @param viewType 407 * <ul> 408 * <li>{@link #VIEW_TYPE_SURFACEVIEW} 409 * <li>{@link #VIEW_TYPE_TEXTUREVIEW} 410 * </ul> 411 */ 412 void onViewTypeChanged(View view, @ViewType int viewType); 413 } 414 415 /** 416 * Interface definition of a callback to be invoked to inform the fullscreen mode is changed. 417 * Application should handle the fullscreen mode accordingly. 418 * @hide TODO remove 419 */ 420 public interface OnFullScreenRequestListener { 421 /** 422 * Called to indicate a fullscreen mode change. 423 */ 424 void onFullScreenRequest(View view, boolean fullScreen); 425 } 426 427 /** 428 * Interface definition of a callback to be invoked to inform that a custom action is performed. 429 * @hide TODO remove 430 */ 431 public interface OnCustomActionListener { 432 /** 433 * Called to indicate that a custom action is performed. 434 * 435 * @param action The action that was originally sent in the 436 * {@link PlaybackState.CustomAction}. 437 * @param extras Optional extras. 438 */ 439 void onCustomAction(String action, Bundle extras); 440 } 441 442 @Override 443 protected void onLayout(boolean changed, int l, int t, int r, int b) { 444 mProvider.onLayout_impl(changed, l, t, r, b); 445 } 446} 447