TransportController.java revision d3a70800e5f2cc2855d53ebea82fb7568affe02a
1d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn/*
2d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * Copyright (C) 2013 The Android Open Source Project
3d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn *
4d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License");
5d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * you may not use this file except in compliance with the License.
6d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * You may obtain a copy of the License at
7d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn *
8d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn *      http://www.apache.org/licenses/LICENSE-2.0
9d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn *
10d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * Unless required by applicable law or agreed to in writing, software
11d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS,
12d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * See the License for the specific language governing permissions and
14d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * limitations under the License.
15d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn */
16d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
17d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornpackage android.support.v4.media;
18d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
19d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.app.Activity;
20d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.content.Context;
21d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.media.AudioManager;
22d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.os.Build;
23d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.support.v4.view.KeyEventCompat;
24d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.view.KeyEvent;
25d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.view.View;
26d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornimport android.widget.MediaController;
27d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
28d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn/**
29d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * Helper for implementing a media transport control (with play, pause, skip, and
30d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * other media actions).  Takes care of both key events and advanced features
31d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn * like {@link android.media.RemoteControlClient}.
32d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn */
33d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackbornpublic class TransportController {
34d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final Context mContext;
35d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final Callbacks mCallbacks;
36d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final AudioManager mAudioManager;
37d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final View mView;
38d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final Object mDispatcherState;
39d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final TransportControllerJellybeanMR2 mController;
40d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final TransportControllerJellybeanMR2.KeyCallback mTransportKeyCallback
41d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            = new TransportControllerJellybeanMR2.KeyCallback() {
42d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public void handleKey(KeyEvent key) {
43d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            key.dispatch(mKeyEventCallback);
44d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
45d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    };
46d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
47d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /** Synonym for {@link KeyEvent#KEYCODE_MEDIA_PLAY KeyEvent.KEYCODE_MEDIA_PLAY} */
48d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public static final int KEYCODE_MEDIA_PLAY = 126;
49d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /** Synonym for {@link KeyEvent#KEYCODE_MEDIA_PAUSE KeyEvent.KEYCODE_MEDIA_PAUSE} */
50d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public static final int KEYCODE_MEDIA_PAUSE = 127;
51d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /** Synonym for {@link KeyEvent#KEYCODE_MEDIA_RECORD KeyEvent.KEYCODE_MEDIA_RECORD} */
52d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public static final int KEYCODE_MEDIA_RECORD = 130;
53d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
54d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    static boolean isMediaKey(int keyCode) {
55d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        switch (keyCode) {
56d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KEYCODE_MEDIA_PLAY:
57d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KEYCODE_MEDIA_PAUSE:
58d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
59d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MUTE:
60d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_HEADSETHOOK:
61d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MEDIA_STOP:
62d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MEDIA_NEXT:
63d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
64d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MEDIA_REWIND:
65d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KEYCODE_MEDIA_RECORD:
66d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
67d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                return true;
68d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            }
69d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
70d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        return false;
71d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
72d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
73d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    final KeyEvent.Callback mKeyEventCallback = new KeyEvent.Callback() {
74d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        @Override
75d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onKeyDown(int keyCode, KeyEvent event) {
76d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return isMediaKey(keyCode) ? mCallbacks.onMediaButtonDown(keyCode, event) : false;
77d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
78d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
79d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onKeyLongPress(int keyCode, KeyEvent event) {
80d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return false;
81d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
82d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
83d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        @Override
84d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onKeyUp(int keyCode, KeyEvent event) {
85d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return isMediaKey(keyCode) ? mCallbacks.onMediaButtonUp(keyCode, event) : false;
86d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
87d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
88d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        @Override
89d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
90d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return false;
91d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
92d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    };
93d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
94d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /**
95d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * Class through which you receive information about media transport actions.
96d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * These may either come from key events dispatched directly to your UI, or
97d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * events sent over a media button event receiver that this class keeps active
98d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * while your window is in focus.
99d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     */
100d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public static class Callbacks {
101d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onMediaButtonDown(int keyCode, KeyEvent event) {
102d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return true;
103d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
104d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onMediaButtonUp(int keyCode, KeyEvent event) {
105d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return true;
106d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
107d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
108d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
109d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /**
110d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * Convenience implementation of {@link Callbacks} that transforms key
111d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * actions to operations on a {@link MediaController.MediaPlayerControl}.
112d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     */
113d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public static class PlayerControlCallbacks extends Callbacks {
114d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        private final MediaController.MediaPlayerControl mPlayerControl;
115d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
116d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public PlayerControlCallbacks(MediaController.MediaPlayerControl playerControl) {
117d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            mPlayerControl = playerControl;
118d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
119d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
120d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        public boolean onMediaButtonDown(int keyCode, KeyEvent event) {
121d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            switch (keyCode) {
122d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                case KEYCODE_MEDIA_PLAY:
123d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    mPlayerControl.start();
124d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    return true;
125d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                case KEYCODE_MEDIA_PAUSE:
126d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                case KeyEvent.KEYCODE_MEDIA_STOP:
127d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    mPlayerControl.pause();
128d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    return true;
129d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
130d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                case KeyEvent.KEYCODE_HEADSETHOOK:
131d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    if (mPlayerControl.isPlaying()) {
132d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                        mPlayerControl.pause();
133d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    } else {
134d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                        mPlayerControl.start();
135d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    }
136d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            }
137d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            return true;
138d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
139d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
140d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
141d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public TransportController(Activity activity, Callbacks callbacks) {
142d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        this(activity, null, callbacks);
143d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
144d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
145d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public TransportController(View view, Callbacks callbacks) {
146d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        this(null, view, callbacks);
147d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
148d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
149d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    private TransportController(Activity activity, View view, Callbacks callbacks) {
150d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        mContext = activity != null ? activity : view.getContext();
151d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        mCallbacks = callbacks;
152d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
153d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        mView = activity != null ? activity.getWindow().getDecorView() : view;
154d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        mDispatcherState = KeyEventCompat.getKeyDispatcherState(mView);
155d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        if (Build.VERSION.SDK_INT >= 18 || Build.VERSION.CODENAME.equals("JellyBeanMR2")) {
156d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            mController = new TransportControllerJellybeanMR2(mContext, mAudioManager,
157d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn                    mView, mTransportKeyCallback);
158d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        } else {
159d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn            mController = null;
160d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        }
161d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
162d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
163d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /**
164d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * Return the {@link android.media.RemoteControlClient} associated with this transport.
165d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * This returns a generic Object since the RemoteControlClient is not availble before
166d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}.  Further, this class
167d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * will not use RemoteControlClient in its implementation until
168d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}.  You should always check for
169d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * null here and not do anything with the RemoteControlClient if none is given; this
170d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * way you don't need to worry about the current platform API version.
171d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     */
172d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public Object getRemoteControlClient() {
173d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        return mController != null ? mController.getRemoteControlClient() : null;
174d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
175d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
176d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /**
177d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * Must call from {@link Activity#dispatchKeyEvent Activity.dispatchKeyEvent} to give
178d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * the transport an opportunity to intercept media keys.  Any such keys will show up
179d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * in {@link Callbacks}.
180d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * @param event
181d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * @return
182d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     */
183d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public boolean dispatchKeyEvent(KeyEvent event) {
184d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        return KeyEventCompat.dispatch(event, mKeyEventCallback, mDispatcherState, this);
185d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
186d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn
187d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    /**
188d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * Optionally call when no longer using the TransportController.  Its resources
189d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * will also be automatically cleaned up when your activity/view is detached from
190d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     * its window, so you don't normally need to call this explicitly.
191d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn     */
192d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    public void destroy() {
193d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn        mController.destroy();
194d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn    }
195d3a70800e5f2cc2855d53ebea82fb7568affe02aDianne Hackborn}
196