1 /* 2 * Copyright 2018 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 17 package com.android.bluetooth.audio_util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.app.PendingIntent; 22 import android.content.Context; 23 import android.media.MediaDescription; 24 import android.media.MediaMetadata; 25 import android.media.Rating; 26 import android.media.session.MediaSession; 27 import android.media.session.PlaybackState; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.ResultReceiver; 32 import android.view.KeyEvent; 33 34 import java.util.List; 35 import java.util.Objects; 36 37 /** 38 * Provide a mockable interface in order to test classes that use MediaController. 39 * We need this class due to the fact that the MediaController class is marked as final and 40 * there is no way to currently mock final classes in Android. Once this is possible this class 41 * can be deleted. 42 */ 43 public class MediaController { 44 @NonNull public android.media.session.MediaController mDelegate; 45 public android.media.session.MediaController.TransportControls mTransportDelegate; 46 public TransportControls mTransportControls; 47 MediaController(@onNull android.media.session.MediaController delegate)48 MediaController(@NonNull android.media.session.MediaController delegate) { 49 mDelegate = delegate; 50 mTransportDelegate = delegate.getTransportControls(); 51 mTransportControls = new TransportControls(); 52 } 53 MediaController(Context context, MediaSession.Token token)54 MediaController(Context context, MediaSession.Token token) { 55 mDelegate = new android.media.session.MediaController(context, token); 56 mTransportDelegate = mDelegate.getTransportControls(); 57 mTransportControls = new TransportControls(); 58 } 59 getWrappedInstance()60 public android.media.session.MediaController getWrappedInstance() { 61 return mDelegate; 62 } 63 64 @NonNull getTransportControls()65 public TransportControls getTransportControls() { 66 return mTransportControls; 67 } 68 69 /** 70 * Wrapper for MediaController.dispatchMediaButtonEvent(KeyEvent keyEvent) 71 */ dispatchMediaButtonEvent(@onNull KeyEvent keyEvent)72 public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) { 73 return mDelegate.dispatchMediaButtonEvent(keyEvent); 74 } 75 76 /** 77 * Wrapper for MediaController.getPlaybackState() 78 */ 79 @Nullable getPlaybackState()80 public PlaybackState getPlaybackState() { 81 return mDelegate.getPlaybackState(); 82 } 83 84 85 /** 86 * Wrapper for MediaController.getMetadata() 87 */ 88 @Nullable getMetadata()89 public MediaMetadata getMetadata() { 90 return mDelegate.getMetadata(); 91 } 92 93 /** 94 * Wrapper for MediaController.getQueue() 95 */ 96 @Nullable getQueue()97 public List<MediaSession.QueueItem> getQueue() { 98 return mDelegate.getQueue(); 99 } 100 101 /** 102 * Wrapper for MediaController.getQueueTitle() 103 */ 104 @Nullable getQueueTitle()105 public CharSequence getQueueTitle() { 106 return mDelegate.getQueueTitle(); 107 } 108 109 /** 110 * Wrapper for MediaController.getExtras() 111 */ 112 @Nullable getExtras()113 public Bundle getExtras() { 114 return mDelegate.getExtras(); 115 } 116 117 /** 118 * Wrapper for MediaController.getRatingType() 119 */ getRatingType()120 public int getRatingType() { 121 return mDelegate.getRatingType(); 122 } 123 124 /** 125 * Wrapper for MediaController.getFlags() 126 */ getFlags()127 public long getFlags() { 128 return mDelegate.getFlags(); 129 } 130 131 /** 132 * Wrapper for MediaController.getPlaybackInfo() 133 */ 134 @Nullable getPlaybackInfo()135 public android.media.session.MediaController.PlaybackInfo getPlaybackInfo() { 136 return mDelegate.getPlaybackInfo(); 137 } 138 139 140 /** 141 * Wrapper for MediaController.getSessionActivity() 142 */ 143 @Nullable getSessionActivity()144 public PendingIntent getSessionActivity() { 145 return mDelegate.getSessionActivity(); 146 } 147 148 /** 149 * Wrapper for MediaController.getSessionToken() 150 */ 151 @NonNull getSessionToken()152 public MediaSession.Token getSessionToken() { 153 return mDelegate.getSessionToken(); 154 } 155 156 /** 157 * Wrapper for MediaController.setVolumeTo(int value, int flags) 158 */ setVolumeTo(int value, int flags)159 public void setVolumeTo(int value, int flags) { 160 mDelegate.setVolumeTo(value, flags); 161 } 162 163 /** 164 * Wrapper for MediaController.adjustVolume(int direction, int flags) 165 */ adjustVolume(int direction, int flags)166 public void adjustVolume(int direction, int flags) { 167 mDelegate.adjustVolume(direction, flags); 168 } 169 170 /** 171 * Wrapper for MediaController.registerCallback(Callback callback) 172 */ registerCallback(@onNull Callback callback)173 public void registerCallback(@NonNull Callback callback) { 174 //TODO(apanicke): Add custom callback struct to be able to analyze and 175 // delegate callbacks 176 mDelegate.registerCallback(callback); 177 } 178 179 /** 180 * Wrapper for MediaController.registerCallback(Callback callback, Handler handler) 181 */ registerCallback(@onNull Callback callback, @Nullable Handler handler)182 public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) { 183 mDelegate.registerCallback(callback, handler); 184 } 185 186 /** 187 * Wrapper for MediaController.unregisterCallback(Callback callback) 188 */ unregisterCallback(@onNull Callback callback)189 public void unregisterCallback(@NonNull Callback callback) { 190 mDelegate.unregisterCallback(callback); 191 } 192 193 /** 194 * Wrapper for MediaController.sendCommand(String command, Bundle args, ResultReceiver cb) 195 */ sendCommand(@onNull String command, @Nullable Bundle args, @Nullable ResultReceiver cb)196 public void sendCommand(@NonNull String command, @Nullable Bundle args, 197 @Nullable ResultReceiver cb) { 198 mDelegate.sendCommand(command, args, cb); 199 } 200 201 /** 202 * Wrapper for MediaController.getPackageName() 203 */ getPackageName()204 public String getPackageName() { 205 return mDelegate.getPackageName(); 206 } 207 208 /** 209 * Wrapper for MediaController.getTag() 210 */ getTag()211 public String getTag() { 212 return mDelegate.getTag(); 213 } 214 215 /** 216 * Wrapper for MediaController.controlsSameSession(MediaController other) 217 */ controlsSameSession(MediaController other)218 public boolean controlsSameSession(MediaController other) { 219 return mDelegate.getSessionToken().equals(other.getWrappedInstance().getSessionToken()); 220 } 221 222 /** 223 * Wrapper for MediaController.controlsSameSession(MediaController other) 224 */ controlsSameSession(android.media.session.MediaController other)225 public boolean controlsSameSession(android.media.session.MediaController other) { 226 return mDelegate.getSessionToken().equals(other.getSessionToken()); 227 } 228 229 /** 230 * Wrapper for MediaController.equals(Object other) 231 */ 232 @Override equals(Object o)233 public boolean equals(Object o) { 234 if (o instanceof android.media.session.MediaController) { 235 return mDelegate.equals(o); 236 } else if (o instanceof MediaController) { 237 MediaController other = (MediaController) o; 238 return mDelegate.equals(other.mDelegate); 239 } 240 return false; 241 } 242 243 /** 244 * Wrapper for MediaController.hashCode() 245 */ 246 @Override hashCode()247 public int hashCode() { 248 return Objects.hash(mDelegate); 249 } 250 251 /** 252 * Wrapper for MediaController.toString() 253 */ 254 @Override toString()255 public String toString() { 256 MediaMetadata data = getMetadata(); 257 MediaDescription desc = (data == null) ? null : data.getDescription(); 258 return "MediaController (" + getPackageName() + "@" + Integer.toHexString( 259 mDelegate.hashCode()) + ") " + desc; 260 } 261 262 /** 263 * Wrapper for MediaController.Callback 264 */ 265 public abstract static class Callback extends android.media.session.MediaController.Callback {} 266 267 /** 268 * Wrapper for MediaController.TransportControls 269 */ 270 public class TransportControls { 271 272 /** 273 * Wrapper for MediaController.TransportControls.prepare() 274 */ prepare()275 public void prepare() { 276 mTransportDelegate.prepare(); 277 } 278 279 /** 280 * Wrapper for MediaController.TransportControls.prepareFromMediaId() 281 */ prepareFromMediaId(String mediaId, Bundle extras)282 public void prepareFromMediaId(String mediaId, Bundle extras) { 283 mTransportDelegate.prepareFromMediaId(mediaId, extras); 284 } 285 286 /** 287 * Wrapper for MediaController.TransportControls.prepareFromSearch() 288 */ prepareFromSearch(String query, Bundle extras)289 public void prepareFromSearch(String query, Bundle extras) { 290 mTransportDelegate.prepareFromSearch(query, extras); 291 } 292 293 /** 294 * Wrapper for MediaController.TransportControls.prepareFromUri() 295 */ prepareFromUri(Uri uri, Bundle extras)296 public void prepareFromUri(Uri uri, Bundle extras) { 297 mTransportDelegate.prepareFromUri(uri, extras); 298 } 299 300 /** 301 * Wrapper for MediaController.TransportControls.play() 302 */ play()303 public void play() { 304 mTransportDelegate.play(); 305 } 306 307 /** 308 * Wrapper for MediaController.TransportControls.playFromMediaId() 309 */ playFromMediaId(String mediaId, Bundle extras)310 public void playFromMediaId(String mediaId, Bundle extras) { 311 mTransportDelegate.playFromMediaId(mediaId, extras); 312 } 313 314 /** 315 * Wrapper for MediaController.TransportControls.playFromSearch() 316 */ playFromSearch(String query, Bundle extras)317 public void playFromSearch(String query, Bundle extras) { 318 mTransportDelegate.playFromSearch(query, extras); 319 } 320 321 /** 322 * Wrapper for MediaController.TransportControls.playFromUri() 323 */ playFromUri(Uri uri, Bundle extras)324 public void playFromUri(Uri uri, Bundle extras) { 325 mTransportDelegate.playFromUri(uri, extras); 326 } 327 328 /** 329 * Wrapper for MediaController.TransportControls.skipToQueueItem() 330 */ skipToQueueItem(long id)331 public void skipToQueueItem(long id) { 332 mTransportDelegate.skipToQueueItem(id); 333 } 334 335 /** 336 * Wrapper for MediaController.TransportControls.pause() 337 */ pause()338 public void pause() { 339 mTransportDelegate.pause(); 340 } 341 342 /** 343 * Wrapper for MediaController.TransportControls.stop() 344 */ stop()345 public void stop() { 346 mTransportDelegate.stop(); 347 } 348 349 /** 350 * Wrapper for MediaController.TransportControls.seekTo() 351 */ seekTo(long pos)352 public void seekTo(long pos) { 353 mTransportDelegate.seekTo(pos); 354 } 355 356 /** 357 * Wrapper for MediaController.TransportControls.fastForward() 358 */ fastForward()359 public void fastForward() { 360 mTransportDelegate.fastForward(); 361 } 362 363 /** 364 * Wrapper for MediaController.TransportControls.skipToNext() 365 */ skipToNext()366 public void skipToNext() { 367 mTransportDelegate.skipToNext(); 368 } 369 370 /** 371 * Wrapper for MediaController.TransportControls.rewind() 372 */ rewind()373 public void rewind() { 374 mTransportDelegate.rewind(); 375 } 376 377 /** 378 * Wrapper for MediaController.TransportControls.skipToPrevious() 379 */ skipToPrevious()380 public void skipToPrevious() { 381 mTransportDelegate.skipToPrevious(); 382 } 383 384 /** 385 * Wrapper for MediaController.TransportControls.setRating() 386 */ setRating(Rating rating)387 public void setRating(Rating rating) { 388 mTransportDelegate.setRating(rating); 389 } 390 391 /** 392 * Wrapper for MediaController.TransportControls.setPlaybackSpeed(float speed) 393 */ setPlaybackSpeed(float speed)394 public void setPlaybackSpeed(float speed) { 395 mTransportDelegate.setPlaybackSpeed(speed); 396 } 397 398 /** 399 * Wrapper for MediaController.TransportControls.sendCustomAction() 400 */ sendCustomAction(@onNull PlaybackState.CustomAction customAction, @Nullable Bundle args)401 public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction, 402 @Nullable Bundle args) { 403 mTransportDelegate.sendCustomAction(customAction, args); 404 } 405 406 /** 407 * Wrapper for MediaController.TransportControls.sendCustomAction() 408 */ sendCustomAction(@onNull String action, @Nullable Bundle args)409 public void sendCustomAction(@NonNull String action, @Nullable Bundle args) { 410 mTransportDelegate.sendCustomAction(action, args); 411 } 412 } 413 } 414 415