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 * &lt;VideoView2
79 *     android:id="@+id/video_view"
80 *     xmlns:widget="http://schemas.android.com/apk/com.android.media.update"
81 *     widget:enableControlView="false" /&gt;
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