TransportMediatorJellybeanMR2.java revision c76d76a0d92a9dca5c91c68b86666d403ac0fd3c
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 android.support.v4.media;
18
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.media.AudioManager;
25import android.media.RemoteControlClient;
26import android.os.SystemClock;
27import android.util.Log;
28import android.view.InputDevice;
29import android.view.KeyCharacterMap;
30import android.view.KeyEvent;
31import android.view.View;
32import android.view.ViewTreeObserver;
33
34public class TransportControllerJellybeanMR2 {
35    final Context mContext;
36    final AudioManager mAudioManager;
37    final View mTargetView;
38    final TransportCallback mTransportCallback;
39    final String mReceiverAction;
40    final IntentFilter mReceiverFilter;
41    final Intent mIntent;
42    final ViewTreeObserver.OnWindowAttachListener mWindowAttachListener =
43            new ViewTreeObserver.OnWindowAttachListener() {
44                @Override
45                public void onWindowAttached() {
46                    windowAttached();
47                }
48                @Override
49                public void onWindowDetached() {
50                    windowDetached();
51                }
52            };
53    final ViewTreeObserver.OnWindowFocusChangeListener mWindowFocusListener =
54            new ViewTreeObserver.OnWindowFocusChangeListener() {
55                @Override
56                public void onWindowFocusChanged(boolean hasFocus) {
57                    if (hasFocus) gainFocus();
58                    else loseFocus();
59                }
60            };
61    final BroadcastReceiver mMediaButtonReceiver = new BroadcastReceiver() {
62        @Override
63        public void onReceive(Context context, Intent intent) {
64            try {
65                KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
66                mTransportCallback.handleKey(event);
67            } catch (ClassCastException e) {
68                Log.w("TransportController", e);
69            }
70        }
71    };
72    AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener
73            = new AudioManager.OnAudioFocusChangeListener() {
74        @Override
75        public void onAudioFocusChange(int focusChange) {
76            mTransportCallback.handleAudioFocusChange(focusChange);
77        }
78    };
79
80    PendingIntent mPendingIntent;
81    RemoteControlClient mRemoteControl;
82    boolean mFocused;
83    int mPlayState = 0;
84    boolean mAudioFocused;
85
86    public interface TransportCallback {
87        public void handleKey(KeyEvent key);
88        public void handleAudioFocusChange(int focusChange);
89    }
90
91    public TransportControllerJellybeanMR2(Context context, AudioManager audioManager,
92            View view, TransportCallback transportCallback) {
93        mContext = context;
94        mAudioManager = audioManager;
95        mTargetView = view;
96        mTransportCallback = transportCallback;
97        mReceiverAction = context.getPackageName() + ":transport:" + System.identityHashCode(this);
98        mIntent = new Intent(mReceiverAction);
99        mIntent.setPackage(context.getPackageName());
100        mReceiverFilter = new IntentFilter();
101        mReceiverFilter.addAction(mReceiverAction);
102        mTargetView.getViewTreeObserver().addOnWindowAttachListener(mWindowAttachListener);
103        mTargetView.getViewTreeObserver().addOnWindowFocusChangeListener(mWindowFocusListener);
104    }
105
106    public Object getRemoteControlClient() {
107        return mRemoteControl;
108    }
109
110    public void destroy() {
111        windowDetached();
112        mTargetView.getViewTreeObserver().removeOnWindowAttachListener(mWindowAttachListener);
113        mTargetView.getViewTreeObserver().removeOnWindowFocusChangeListener(mWindowFocusListener);
114    }
115
116    void windowAttached() {
117        mContext.registerReceiver(mMediaButtonReceiver, mReceiverFilter);
118        mPendingIntent = PendingIntent.getBroadcast(mContext, 0, mIntent,
119                PendingIntent.FLAG_CANCEL_CURRENT);
120        mRemoteControl = new RemoteControlClient(mPendingIntent);
121    }
122
123    void gainFocus() {
124        if (!mFocused) {
125            mFocused = true;
126            mAudioManager.registerMediaButtonEventReceiver(mPendingIntent);
127            mAudioManager.registerRemoteControlClient(mRemoteControl);
128            if (mPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
129                takeAudioFocus();
130            }
131        }
132    }
133
134    void takeAudioFocus() {
135        if (!mAudioFocused) {
136            mAudioFocused = true;
137            mAudioManager.requestAudioFocus(mAudioFocusChangeListener,
138                    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
139        }
140    }
141
142    public void startPlaying() {
143        if (mPlayState != RemoteControlClient.PLAYSTATE_PLAYING) {
144            mPlayState = RemoteControlClient.PLAYSTATE_PLAYING;
145            mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING);
146        }
147        if (mFocused) {
148            takeAudioFocus();
149        }
150    }
151
152    public void pausePlaying() {
153        if (mPlayState == RemoteControlClient.PLAYSTATE_PLAYING) {
154            mPlayState = RemoteControlClient.PLAYSTATE_PAUSED;
155            mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
156        }
157    }
158
159    public void stopPlaying() {
160        if (mPlayState != RemoteControlClient.PLAYSTATE_STOPPED) {
161            mPlayState = RemoteControlClient.PLAYSTATE_STOPPED;
162            mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED);
163        }
164        dropAudioFocus();
165    }
166
167    void handleAudioFocusChange(int focusChange) {
168        int keyCode = 0;
169        switch (focusChange) {
170            case AudioManager.AUDIOFOCUS_GAIN:
171                keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
172                break;
173            case AudioManager.AUDIOFOCUS_LOSS:
174                keyCode = KeyEvent.KEYCODE_MEDIA_STOP;
175                break;
176            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
177            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
178                keyCode = KeyEvent.KEYCODE_MEDIA_PAUSE;
179                break;
180        }
181        if (keyCode != 0) {
182            final long now = SystemClock.uptimeMillis();
183            mTransportCallback.handleKey(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
184                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
185            mTransportCallback.handleKey(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
186                    KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
187        }
188    }
189
190    void dropAudioFocus() {
191        if (mAudioFocused) {
192            mAudioFocused = false;
193            mAudioManager.abandonAudioFocus(mAudioFocusChangeListener);
194        }
195    }
196
197    void loseFocus() {
198        dropAudioFocus();
199        if (mFocused) {
200            mFocused = false;
201            mAudioManager.unregisterRemoteControlClient(mRemoteControl);
202            mAudioManager.unregisterMediaButtonEventReceiver(mPendingIntent);
203        }
204    }
205
206    void windowDetached() {
207        loseFocus();
208        if (mPendingIntent != null) {
209            mContext.unregisterReceiver(mMediaButtonReceiver);
210            mPendingIntent.cancel();
211            mPendingIntent = null;
212            mRemoteControl = null;
213        }
214    }
215}
216