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