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