1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser;
6
7import android.content.Context;
8import android.graphics.Color;
9import android.view.KeyEvent;
10import android.view.MotionEvent;
11import android.view.SurfaceHolder;
12import android.view.SurfaceView;
13import android.view.View;
14import android.widget.MediaController;
15
16/**
17 * Adds Android media controller to ContentVideoView.
18 * The sole purpose of this class is to maintain legacy behavior while we test
19 * the blink-based media controller.
20 * https://code.google.com/p/chromium/issues/detail?id=331966
21 */
22public class ContentVideoViewLegacy extends ContentVideoView {
23    private FullScreenMediaController mMediaController;
24    private boolean mCanPause;
25    private boolean mCanSeekBackward;
26    private boolean mCanSeekForward;
27    private int mCurrentBufferPercentage;
28    private MediaControlsVisibilityListener mListener;
29
30    /**
31     * A listener for changes in the MediaController visibility.
32     */
33    public interface MediaControlsVisibilityListener {
34        /**
35         * Callback for when the visibility of the media controls changes.
36         *
37         * @param shown true if the media controls are shown to the user, false otherwise
38         */
39        public void onMediaControlsVisibilityChanged(boolean shown);
40    }
41
42    private static class FullScreenMediaController extends MediaController {
43
44        final View mVideoView;
45        final MediaControlsVisibilityListener mListener;
46
47        /**
48         * @param context The context.
49         * @param video The full screen video container view.
50         * @param listener A listener that listens to the visibility of media controllers.
51         */
52        public FullScreenMediaController(
53                Context context,
54                View video,
55                MediaControlsVisibilityListener listener) {
56            super(context);
57            mVideoView = video;
58            mListener = listener;
59        }
60
61        @Override
62        public void show() {
63            super.show();
64            if (mListener != null) mListener.onMediaControlsVisibilityChanged(true);
65            if (mVideoView != null) {
66                mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
67            }
68        }
69
70        @Override
71        public void hide() {
72            if (mVideoView != null) {
73                mVideoView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
74            }
75            if (mListener != null)  mListener.onMediaControlsVisibilityChanged(false);
76            super.hide();
77        }
78    }
79
80    ContentVideoViewLegacy(Context context, long nativeContentVideoView,
81            ContentVideoViewClient client) {
82        super(context, nativeContentVideoView, client);
83        setBackgroundColor(Color.BLACK);
84        mCurrentBufferPercentage = 0;
85    }
86
87    @Override
88    protected void showContentVideoView() {
89        SurfaceView surfaceView = getSurfaceView();
90        surfaceView.setZOrderOnTop(true);
91        surfaceView.setOnKeyListener(new OnKeyListener() {
92            @Override
93            public boolean onKey(View v, int keyCode, KeyEvent event) {
94                boolean isKeyCodeSupported = (
95                        keyCode != KeyEvent.KEYCODE_BACK &&
96                        keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
97                        keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
98                        keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&
99                        keyCode != KeyEvent.KEYCODE_CALL &&
100                        keyCode != KeyEvent.KEYCODE_MENU &&
101                        keyCode != KeyEvent.KEYCODE_SEARCH &&
102                        keyCode != KeyEvent.KEYCODE_ENDCALL);
103                if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
104                    if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
105                            keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
106                        if (isPlaying()) {
107                            pause();
108                            mMediaController.show();
109                        } else {
110                            start();
111                            mMediaController.hide();
112                        }
113                        return true;
114                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
115                        if (!isPlaying()) {
116                            start();
117                            mMediaController.hide();
118                        }
119                        return true;
120                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
121                            || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
122                        if (isPlaying()) {
123                            pause();
124                            mMediaController.show();
125                        }
126                        return true;
127                    } else {
128                        toggleMediaControlsVisiblity();
129                    }
130                } else if (keyCode == KeyEvent.KEYCODE_BACK &&
131                        event.getAction() == KeyEvent.ACTION_UP) {
132                    exitFullscreen(false);
133                    return true;
134                } else if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_SEARCH) {
135                    return true;
136                }
137                return false;
138            }
139        });
140        setOnTouchListener(new OnTouchListener() {
141            @Override
142            public boolean onTouch(View v, MotionEvent event) {
143                if (isInPlaybackState() && mMediaController != null &&
144                        event.getAction() == MotionEvent.ACTION_DOWN) {
145                    toggleMediaControlsVisiblity();
146                }
147                return true;
148            }
149        });
150        surfaceView.setFocusable(true);
151        surfaceView.setFocusableInTouchMode(true);
152        surfaceView.requestFocus();
153        super.showContentVideoView();
154    }
155
156    @Override
157    public void onMediaPlayerError(int errorType) {
158        super.onMediaPlayerError(errorType);
159        if (errorType == MEDIA_ERROR_INVALID_CODE) return;
160        if (mMediaController != null) mMediaController.hide();
161    }
162
163    @Override
164    protected void onBufferingUpdate(int percent) {
165        super.onBufferingUpdate(percent);
166        mCurrentBufferPercentage = percent;
167    }
168
169    @Override
170    protected void onUpdateMediaMetadata(
171            int videoWidth,
172            int videoHeight,
173            int duration,
174            boolean canPause,
175            boolean canSeekBack,
176            boolean canSeekForward) {
177        super.onUpdateMediaMetadata(videoWidth, videoHeight, duration,
178                canPause, canSeekBack, canSeekForward);
179        mCanPause = canPause;
180        mCanSeekBackward = canSeekBack;
181        mCanSeekForward = canSeekForward;
182
183        if (mMediaController == null) return;
184
185        mMediaController.setEnabled(true);
186        // If paused , should show the controller forever.
187        if (isPlaying()) {
188            mMediaController.show();
189        } else {
190            mMediaController.show(0);
191        }
192    }
193
194    @Override
195    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
196        super.surfaceChanged(holder, format, width, height);
197        SurfaceView surfaceView = getSurfaceView();
198        surfaceView.setFocusable(true);
199        surfaceView.setFocusableInTouchMode(true);
200        if (isInPlaybackState() && mMediaController != null) {
201            mMediaController.show();
202        }
203    }
204
205    @Override
206    protected void openVideo() {
207        super.openVideo();
208
209        mCurrentBufferPercentage = 0;
210
211        if (mMediaController != null) return;
212        mMediaController = new FullScreenMediaController(getContext(), this, mListener);
213        mMediaController.setMediaPlayer(new MediaController.MediaPlayerControl() {
214            @Override public boolean canPause() { return mCanPause; }
215            @Override public boolean canSeekBackward() { return mCanSeekBackward; }
216            @Override public boolean canSeekForward() { return mCanSeekForward; }
217            @Override public int getAudioSessionId() { return 0; }
218            @Override public int getBufferPercentage() { return mCurrentBufferPercentage; }
219
220            @Override
221            public int getCurrentPosition() {
222                return ContentVideoViewLegacy.this.getCurrentPosition();
223            }
224
225            @Override
226            public int getDuration() {
227                return ContentVideoViewLegacy.this.getDuration();
228            }
229
230            @Override
231            public boolean isPlaying() {
232                return ContentVideoViewLegacy.this.isPlaying();
233            }
234
235            @Override
236            public void pause() {
237                ContentVideoViewLegacy.this.pause();
238            }
239
240            @Override
241            public void seekTo(int pos) {
242                ContentVideoViewLegacy.this.seekTo(pos);
243            }
244
245            @Override
246            public void start() {
247                ContentVideoViewLegacy.this.start();
248            }
249        });
250        mMediaController.setAnchorView(getSurfaceView());
251        mMediaController.setEnabled(false);
252    }
253
254    @Override
255    protected void onCompletion() {
256        super.onCompletion();
257        if (mMediaController != null) {
258            mMediaController.hide();
259        }
260    }
261
262    @Override
263    public boolean onTrackballEvent(MotionEvent ev) {
264        if (isInPlaybackState() && mMediaController != null) {
265            toggleMediaControlsVisiblity();
266        }
267        return false;
268    }
269
270    private void toggleMediaControlsVisiblity() {
271        if (mMediaController.isShowing()) {
272            mMediaController.hide();
273        } else {
274            mMediaController.show();
275        }
276    }
277
278    @Override
279    protected void destroyContentVideoView(boolean nativeViewDestroyed) {
280        if (mMediaController != null) {
281            mMediaController.setEnabled(false);
282            mMediaController.hide();
283            mMediaController = null;
284        }
285        super.destroyContentVideoView(nativeViewDestroyed);
286    }
287
288    @Override
289    public boolean onTouchEvent(MotionEvent ev) {
290        return true;
291    }
292
293    /**
294     * Sets the MediaControlsVisibilityListener that wants to listen to visibility change events.
295     *
296     * @param listener the listener to send the events to.
297     */
298    public void setListener(MediaControlsVisibilityListener listener) {
299        mListener = listener;
300    }
301
302}
303