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.os.SystemClock; 20import android.view.KeyEvent; 21 22/** 23 * Implemented by the playback side of the media system, to respond to 24 * requests to perform actions and to retrieve its current state. These 25 * requests may either come from key events dispatched directly to your UI, or 26 * events sent over a media button event receiver that this class keeps active 27 * while your window is in focus. 28 */ 29public abstract class TransportPerformer { 30 /** 31 * Request to start playback on the media, resuming from whatever current state 32 * (position etc) it is in. 33 */ 34 public abstract void onStart(); 35 36 /** 37 * Request to pause playback of the media, staying at the current playback position 38 * and other state so a later call to {@link #onStart()} will resume at the same place. 39 */ 40 public abstract void onPause(); 41 42 /** 43 * Request to completely stop playback of the media, clearing whatever state the 44 * player thinks is appropriate. 45 */ 46 public abstract void onStop(); 47 48 /** 49 * Request to return the duration of the current media, in milliseconds. 50 */ 51 public abstract long onGetDuration(); 52 53 /** 54 * Request to return the current playback position, in milliseconds. 55 */ 56 public abstract long onGetCurrentPosition(); 57 58 /** 59 * Request to move the current playback position. 60 * @param pos New position to move to, in milliseconds. 61 */ 62 public abstract void onSeekTo(long pos); 63 64 /** 65 * Request to find out whether the player is currently playing its media. 66 */ 67 public abstract boolean onIsPlaying(); 68 69 /** 70 * Request to find out how much of the media has been buffered on the local device. 71 * @return Return a percentage (0-100) indicating how much of the total data 72 * has been buffered. The default implementation returns 100, meaning the content 73 * is always on the local device. 74 */ 75 public int onGetBufferPercentage() { 76 return 100; 77 } 78 79 /** 80 * Retrieves the flags for the media transport control buttons that this transport supports. 81 * Result is a combination of the following flags: 82 * {@link TransportMediator#FLAG_KEY_MEDIA_PREVIOUS}, 83 * {@link TransportMediator#FLAG_KEY_MEDIA_REWIND}, 84 * {@link TransportMediator#FLAG_KEY_MEDIA_PLAY}, 85 * {@link TransportMediator#FLAG_KEY_MEDIA_PLAY_PAUSE}, 86 * {@link TransportMediator#FLAG_KEY_MEDIA_PAUSE}, 87 * {@link TransportMediator#FLAG_KEY_MEDIA_STOP}, 88 * {@link TransportMediator#FLAG_KEY_MEDIA_FAST_FORWARD}, 89 * {@link TransportMediator#FLAG_KEY_MEDIA_NEXT} 90 * 91 * <p>The default implementation returns: 92 * {@link TransportMediator#FLAG_KEY_MEDIA_PLAY}, 93 * {@link TransportMediator#FLAG_KEY_MEDIA_PLAY_PAUSE}, 94 * {@link TransportMediator#FLAG_KEY_MEDIA_PAUSE}, and 95 * {@link TransportMediator#FLAG_KEY_MEDIA_STOP}</p> 96 */ 97 public int onGetTransportControlFlags() { 98 return TransportMediator.FLAG_KEY_MEDIA_PLAY 99 | TransportMediator.FLAG_KEY_MEDIA_PLAY_PAUSE 100 | TransportMediator.FLAG_KEY_MEDIA_PAUSE 101 | TransportMediator.FLAG_KEY_MEDIA_STOP; 102 } 103 104 /** 105 * Report that a media button has been pressed. This is like 106 * {@link android.view.KeyEvent.Callback#onKeyDown(int, android.view.KeyEvent)} but 107 * will only deliver media keys. The default implementation handles these keys: 108 * <ul> 109 * <li>KEYCODE_MEDIA_PLAY: call {@link #onStart}</li> 110 * <li>KEYCODE_MEDIA_PAUSE: call {@link #onPause}</li> 111 * <li>KEYCODE_MEDIA_STOP: call {@link #onStop}</li> 112 * <li>KEYCODE_MEDIA_PLAY_PAUSE and KEYCODE_HEADSETHOOK: call {@link #onPause} 113 * if {@link #onIsPlaying()} returns true, otherwise call {@link #onStart}</li> 114 * </ul> 115 * @param keyCode The code of the media key. 116 * @param event The full key event. 117 * @return Indicate whether the key has been consumed. The default 118 * implementation always returns true. This only matters for keys 119 * being dispatched here from 120 * {@link TransportMediator#dispatchKeyEvent(android.view.KeyEvent) 121 * TransportController.dispatchKeyEvent}, and determines whether the key 122 * continues on to its default key handling (which for media keys means 123 * being delivered to the current media remote control, which should 124 * be us). 125 */ 126 public boolean onMediaButtonDown(int keyCode, KeyEvent event) { 127 switch (keyCode) { 128 case TransportMediator.KEYCODE_MEDIA_PLAY: 129 onStart(); 130 return true; 131 case TransportMediator.KEYCODE_MEDIA_PAUSE: 132 onPause(); 133 return true; 134 case KeyEvent.KEYCODE_MEDIA_STOP: 135 onStop(); 136 return true; 137 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 138 case KeyEvent.KEYCODE_HEADSETHOOK: 139 if (onIsPlaying()) { 140 onPause(); 141 } else { 142 onStart(); 143 } 144 } 145 return true; 146 } 147 148 /** 149 * Report that a media button has been released. This is like 150 * {@link KeyEvent.Callback#onKeyUp(int, android.view.KeyEvent)} but 151 * will only deliver media keys. The default implementation does nothing. 152 * @param keyCode The code of the media key. 153 * @param event The full key event. 154 * @return Indicate whether the key has been consumed. The default 155 * implementation always returns true. This only matters for keys 156 * being dispatched here from 157 * {@link TransportMediator#dispatchKeyEvent(android.view.KeyEvent) 158 * TransportController.dispatchKeyEvent}, and determines whether the key 159 * continues on to its default key handling (which for media keys means 160 * being delivered to the current media remote control, which should 161 * be us). 162 */ 163 public boolean onMediaButtonUp(int keyCode, KeyEvent event) { 164 return true; 165 } 166 167 // Copy constants from framework since we can't link to them. 168 static final int AUDIOFOCUS_GAIN = 1; 169 static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; 170 static final int AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK = 3; 171 static final int AUDIOFOCUS_LOSS = -1 * AUDIOFOCUS_GAIN; 172 static final int AUDIOFOCUS_LOSS_TRANSIENT = -1 * AUDIOFOCUS_GAIN_TRANSIENT; 173 static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = 174 -1 * AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; 175 176 /** 177 * Report that audio focus has changed on the app. This only happens if 178 * you have indicated you have started playing with 179 * {@link TransportMediator#startPlaying TransportController.startPlaying}, 180 * which takes audio focus for you. 181 * @param focusChange The type of focus change, as per 182 * {@link android.media.AudioManager.OnAudioFocusChangeListener#onAudioFocusChange(int) 183 * OnAudioFocusChangeListener.onAudioFocusChange}. The default implementation will 184 * deliver a {@link KeyEvent#KEYCODE_MEDIA_STOP} 185 * when receiving {@link android.media.AudioManager#AUDIOFOCUS_LOSS}. 186 */ 187 public void onAudioFocusChange(int focusChange) { 188 int keyCode = 0; 189 switch (focusChange) { 190 case AUDIOFOCUS_LOSS: 191 // This will cause us to stop playback, which means we drop audio focus 192 // so we will not get any further audio focus gain. 193 keyCode = TransportMediator.KEYCODE_MEDIA_PAUSE; 194 break; 195 } 196 if (keyCode != 0) { 197 final long now = SystemClock.uptimeMillis(); 198 onMediaButtonDown(keyCode, new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0)); 199 onMediaButtonUp(keyCode, new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0)); 200 } 201 } 202} 203