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