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