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