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