TransportControllerActivity.java revision 1337277f8f8802064f42bed895695758f40b50d9
1/*
2 * Copyright (C) 2013 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 com.example.android.supportv4.media;
18
19import com.example.android.supportv4.R;
20
21import android.app.ActionBar;
22import android.content.Context;
23import android.media.MediaPlayer;
24import android.os.Handler;
25import android.util.AttributeSet;
26import android.view.KeyEvent;
27import android.view.View;
28
29import android.app.Activity;
30import android.net.Uri;
31import android.os.Bundle;
32import android.widget.VideoView;
33
34import android.support.v4.media.TransportController;
35
36public class TransportControllerActivity extends Activity {
37
38    /**
39     * TODO: Set the path variable to a streaming video URL or a local media
40     * file path.
41     */
42    private Content mContent;
43    private TransportController mTransportController;
44    private MediaController mMediaController;
45
46    /**
47     * Handle media buttons to start/stop video playback.  Real implementations
48     * will probably handle more buttons, like skip and fast-forward.
49     */
50    TransportController.Callbacks mTransportCallbacks = new TransportController.Callbacks() {
51        public boolean onMediaButtonDown(int keyCode, KeyEvent event) {
52            switch (keyCode) {
53                case TransportController.KEYCODE_MEDIA_PLAY:
54                    mMediaPlayerControl.start();
55                    return true;
56                case TransportController.KEYCODE_MEDIA_PAUSE:
57                case KeyEvent.KEYCODE_MEDIA_STOP:
58                    mMediaPlayerControl.pause();
59                    return true;
60                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
61                case KeyEvent.KEYCODE_HEADSETHOOK:
62                    if (mContent.isPlaying()) {
63                        mMediaPlayerControl.pause();
64                    } else {
65                        mMediaPlayerControl.start();
66                    }
67            }
68            return true;
69        }
70    };
71
72    /**
73     * Handle actions from on-screen media controls.  Most of these are simple re-directs
74     * to the VideoView; some we need to capture to update our state.
75     */
76    MediaController.MediaPlayerControl mMediaPlayerControl
77            = new MediaController.MediaPlayerControl() {
78
79        @Override
80        public void start() {
81            mTransportController.startPlaying();
82            mContent.start();
83        }
84
85        @Override
86        public void pause() {
87            mTransportController.pausePlaying();
88            mContent.pause();
89        }
90
91        @Override
92        public int getDuration() {
93            return mContent.getDuration();
94        }
95
96        @Override
97        public int getCurrentPosition() {
98            return mContent.getCurrentPosition();
99        }
100
101        @Override
102        public void seekTo(int pos) {
103            mContent.seekTo(pos);
104        }
105
106        @Override
107        public boolean isPlaying() {
108            return mContent.isPlaying();
109        }
110
111        @Override
112        public int getBufferPercentage() {
113            return mContent.getBufferPercentage();
114        }
115
116        @Override
117        public boolean canPause() {
118            return mContent.canPause();
119        }
120
121        @Override
122        public boolean canSeekBackward() {
123            return mContent.canSeekBackward();
124        }
125
126        @Override
127        public boolean canSeekForward() {
128            return mContent.canSeekForward();
129        }
130    };
131
132    /**
133     * This is the actual video player.  It is the top-level content of
134     * the activity's view hierarchy, going under the status bar and nav
135     * bar areas.
136     */
137    public static class Content extends VideoView implements
138            View.OnSystemUiVisibilityChangeListener, View.OnClickListener,
139            ActionBar.OnMenuVisibilityListener, MediaPlayer.OnPreparedListener,
140            MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
141        Activity mActivity;
142        TransportController mTransportController;
143        MediaController mMediaController;
144        boolean mAddedMenuListener;
145        boolean mMenusOpen;
146        boolean mPaused;
147        boolean mNavVisible;
148        int mLastSystemUiVis;
149
150        Runnable mNavHider = new Runnable() {
151            @Override public void run() {
152                setNavVisibility(false);
153            }
154        };
155
156        Runnable mProgressUpdater = new Runnable() {
157            @Override public void run() {
158                mMediaController.updateProgress();
159                getHandler().postDelayed(this, 1000);
160            }
161        };
162
163        public Content(Context context, AttributeSet attrs) {
164            super(context, attrs);
165            setOnSystemUiVisibilityChangeListener(this);
166            setOnClickListener(this);
167            setOnPreparedListener(this);
168            setOnCompletionListener(this);
169            setOnErrorListener(this);
170        }
171
172        public void init(Activity activity, TransportController transportController,
173                MediaController mediaController) {
174            // This called by the containing activity to supply the surrounding
175            // state of the video player that it will interact with.
176            mActivity = activity;
177            mTransportController = transportController;
178            mMediaController = mediaController;
179            pause();
180        }
181
182        @Override protected void onAttachedToWindow() {
183            super.onAttachedToWindow();
184            if (mActivity != null) {
185                mAddedMenuListener = true;
186                mActivity.getActionBar().addOnMenuVisibilityListener(this);
187            }
188        }
189
190        @Override protected void onDetachedFromWindow() {
191            super.onDetachedFromWindow();
192            if (mAddedMenuListener) {
193                mActivity.getActionBar().removeOnMenuVisibilityListener(this);
194            }
195            mNavVisible = false;
196        }
197
198        @Override public void onSystemUiVisibilityChange(int visibility) {
199            // Detect when we go out of nav-hidden mode, to clear our state
200            // back to having the full UI chrome up.  Only do this when
201            // the state is changing and nav is no longer hidden.
202            int diff = mLastSystemUiVis ^ visibility;
203            mLastSystemUiVis = visibility;
204            if ((diff&SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
205                    && (visibility&SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
206                setNavVisibility(true);
207            }
208        }
209
210        @Override protected void onWindowVisibilityChanged(int visibility) {
211            super.onWindowVisibilityChanged(visibility);
212
213            // When we become visible or invisible, play is paused.
214            pause();
215        }
216
217        @Override public void onClick(View v) {
218            // Clicking anywhere makes the navigation visible.
219            setNavVisibility(true);
220        }
221
222        @Override public void onMenuVisibilityChanged(boolean isVisible) {
223            mMenusOpen = isVisible;
224            setNavVisibility(true);
225        }
226
227        @Override
228        public void onPrepared(MediaPlayer mp) {
229            mMediaController.setEnabled(true);
230        }
231
232        @Override
233        public void onCompletion(MediaPlayer mp) {
234            mTransportController.pausePlaying();
235            pause();
236        }
237
238        @Override
239        public boolean onError(MediaPlayer mp, int what, int extra) {
240            mTransportController.pausePlaying();
241            pause();
242            return false;
243        }
244
245        @Override public void start() {
246            super.start();
247            mPaused = false;
248            setKeepScreenOn(true);
249            setNavVisibility(true);
250            mMediaController.refresh();
251            scheduleProgressUpdater();
252        }
253
254        @Override public void pause() {
255            super.pause();
256            mPaused = true;
257            setKeepScreenOn(false);
258            setNavVisibility(true);
259            mMediaController.refresh();
260            scheduleProgressUpdater();
261        }
262
263        void scheduleProgressUpdater() {
264            Handler h = getHandler();
265            if (h != null) {
266                if (mNavVisible && !mPaused) {
267                    h.removeCallbacks(mProgressUpdater);
268                    h.post(mProgressUpdater);
269                } else {
270                    h.removeCallbacks(mProgressUpdater);
271                }
272            }
273        }
274
275        void setNavVisibility(boolean visible) {
276            int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
277                    | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
278                    | SYSTEM_UI_FLAG_LAYOUT_STABLE;
279            if (!visible) {
280                newVis |= SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_FULLSCREEN
281                        | SYSTEM_UI_FLAG_HIDE_NAVIGATION;
282            }
283
284            // If we are now visible, schedule a timer for us to go invisible.
285            if (visible) {
286                Handler h = getHandler();
287                if (h != null) {
288                    h.removeCallbacks(mNavHider);
289                    if (!mMenusOpen && !mPaused) {
290                        // If the menus are open or play is paused, we will not auto-hide.
291                        h.postDelayed(mNavHider, 3000);
292                    }
293                }
294            }
295
296            // Set the new desired visibility.
297            setSystemUiVisibility(newVis);
298            mNavVisible = visible;
299            mMediaController.setVisibility(visible ? VISIBLE : INVISIBLE);
300            scheduleProgressUpdater();
301        }
302    }
303
304    @Override
305    public void onCreate(Bundle icicle) {
306        super.onCreate(icicle);
307        setContentView(R.layout.videoview);
308
309        // Find the video player in our UI.
310        mContent = (Content) findViewById(R.id.content);
311
312        // Create and initialize the media control UI.
313        mMediaController = (MediaController) findViewById(R.id.media_controller);
314        mMediaController.setMediaPlayer(mMediaPlayerControl);
315
316        // Create transport controller to control video, giving the callback
317        // interface to receive actions from.
318        mTransportController = new TransportController(this, mTransportCallbacks);
319
320        // We're just playing a built-in demo video.
321        mContent.init(this, mTransportController, mMediaController);
322        mContent.setVideoURI(Uri.parse("android.resource://" + getPackageName() +
323                "/" + R.raw.videoviewdemo));
324    }
325
326    @Override
327    public boolean dispatchKeyEvent(KeyEvent event) {
328        // We first dispatch keys to the transport controller -- we want it
329        // to get to consume any media keys rather than letting whoever has focus
330        // in the view hierarchy to potentially eat it.
331        if (mTransportController.dispatchKeyEvent(event)) {
332            return true;
333        }
334
335        return super.dispatchKeyEvent(event);
336    }
337}
338