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. We need this
39  * class due to the fact that the MediaController class is marked as final and there is no way to
40  * currently mock final classes in Android. Once this is possible this class 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     /** Wrapper for MediaController.dispatchMediaButtonEvent(KeyEvent keyEvent) */
dispatchMediaButtonEvent(@onNull KeyEvent keyEvent)69     public boolean dispatchMediaButtonEvent(@NonNull KeyEvent keyEvent) {
70         return mDelegate.dispatchMediaButtonEvent(keyEvent);
71     }
72 
73     /** Wrapper for MediaController.getPlaybackState() */
74     @Nullable
getPlaybackState()75     public PlaybackState getPlaybackState() {
76         return mDelegate.getPlaybackState();
77     }
78 
79     /** Wrapper for MediaController.getMetadata() */
80     @Nullable
getMetadata()81     public MediaMetadata getMetadata() {
82         return mDelegate.getMetadata();
83     }
84 
85     /** Wrapper for MediaController.getQueue() */
86     @Nullable
getQueue()87     public List<MediaSession.QueueItem> getQueue() {
88         return mDelegate.getQueue();
89     }
90 
91     /** Wrapper for MediaController.getQueueTitle() */
92     @Nullable
getQueueTitle()93     public CharSequence getQueueTitle() {
94         return mDelegate.getQueueTitle();
95     }
96 
97     /** Wrapper for MediaController.getExtras() */
98     @Nullable
getExtras()99     public Bundle getExtras() {
100         return mDelegate.getExtras();
101     }
102 
103     /** Wrapper for MediaController.getRatingType() */
getRatingType()104     public int getRatingType() {
105         return mDelegate.getRatingType();
106     }
107 
108     /** Wrapper for MediaController.getFlags() */
getFlags()109     public long getFlags() {
110         return mDelegate.getFlags();
111     }
112 
113     /** Wrapper for MediaController.getPlaybackInfo() */
114     @Nullable
getPlaybackInfo()115     public android.media.session.MediaController.PlaybackInfo getPlaybackInfo() {
116         return mDelegate.getPlaybackInfo();
117     }
118 
119     /** Wrapper for MediaController.getSessionActivity() */
120     @Nullable
getSessionActivity()121     public PendingIntent getSessionActivity() {
122         return mDelegate.getSessionActivity();
123     }
124 
125     /** Wrapper for MediaController.getSessionToken() */
126     @NonNull
getSessionToken()127     public MediaSession.Token getSessionToken() {
128         return mDelegate.getSessionToken();
129     }
130 
131     /** Wrapper for MediaController.setVolumeTo(int value, int flags) */
setVolumeTo(int value, int flags)132     public void setVolumeTo(int value, int flags) {
133         mDelegate.setVolumeTo(value, flags);
134     }
135 
136     /** Wrapper for MediaController.adjustVolume(int direction, int flags) */
adjustVolume(int direction, int flags)137     public void adjustVolume(int direction, int flags) {
138         mDelegate.adjustVolume(direction, flags);
139     }
140 
141     /** Wrapper for MediaController.registerCallback(Callback callback) */
registerCallback(@onNull Callback callback)142     public void registerCallback(@NonNull Callback callback) {
143         // TODO(apanicke): Add custom callback struct to be able to analyze and
144         // delegate callbacks
145         mDelegate.registerCallback(callback);
146     }
147 
148     /** Wrapper for MediaController.registerCallback(Callback callback, Handler handler) */
registerCallback(@onNull Callback callback, @Nullable Handler handler)149     public void registerCallback(@NonNull Callback callback, @Nullable Handler handler) {
150         mDelegate.registerCallback(callback, handler);
151     }
152 
153     /** Wrapper for MediaController.unregisterCallback(Callback callback) */
unregisterCallback(@onNull Callback callback)154     public void unregisterCallback(@NonNull Callback callback) {
155         mDelegate.unregisterCallback(callback);
156     }
157 
158     /** Wrapper for MediaController.sendCommand(String command, Bundle args, ResultReceiver cb) */
sendCommand( @onNull String command, @Nullable Bundle args, @Nullable ResultReceiver cb)159     public void sendCommand(
160             @NonNull String command, @Nullable Bundle args, @Nullable ResultReceiver cb) {
161         mDelegate.sendCommand(command, args, cb);
162     }
163 
164     /** Wrapper for MediaController.getPackageName() */
getPackageName()165     public String getPackageName() {
166         return mDelegate.getPackageName();
167     }
168 
169     /** Wrapper for MediaController.getTag() */
getTag()170     public String getTag() {
171         return mDelegate.getTag();
172     }
173 
174     /** Wrapper for MediaController.controlsSameSession(MediaController other) */
controlsSameSession(MediaController other)175     public boolean controlsSameSession(MediaController other) {
176         return mDelegate.getSessionToken().equals(other.getWrappedInstance().getSessionToken());
177     }
178 
179     /** Wrapper for MediaController.controlsSameSession(MediaController other) */
controlsSameSession(android.media.session.MediaController other)180     public boolean controlsSameSession(android.media.session.MediaController other) {
181         return mDelegate.getSessionToken().equals(other.getSessionToken());
182     }
183 
184     /** Wrapper for MediaController.equals(Object other) */
185     @Override
equals(Object o)186     public boolean equals(Object o) {
187         if (o instanceof android.media.session.MediaController) {
188             return mDelegate.equals(o);
189         } else if (o instanceof MediaController) {
190             MediaController other = (MediaController) o;
191             return mDelegate.equals(other.mDelegate);
192         }
193         return false;
194     }
195 
196     /** Wrapper for MediaController.hashCode() */
197     @Override
hashCode()198     public int hashCode() {
199         return Objects.hash(mDelegate);
200     }
201 
202     /** Wrapper for MediaController.toString() */
203     @Override
toString()204     public String toString() {
205         MediaMetadata data = getMetadata();
206         MediaDescription desc = (data == null) ? null : data.getDescription();
207         return "MediaController ("
208                 + getPackageName()
209                 + "@"
210                 + Integer.toHexString(mDelegate.hashCode())
211                 + ") "
212                 + desc;
213     }
214 
215     /** Wrapper for MediaController.Callback */
216     public abstract static class Callback extends android.media.session.MediaController.Callback {}
217 
218     /** Wrapper for MediaController.TransportControls */
219     public class TransportControls {
220 
221         /** Wrapper for MediaController.TransportControls.prepare() */
prepare()222         public void prepare() {
223             mTransportDelegate.prepare();
224         }
225 
226         /** Wrapper for MediaController.TransportControls.prepareFromMediaId() */
prepareFromMediaId(String mediaId, Bundle extras)227         public void prepareFromMediaId(String mediaId, Bundle extras) {
228             mTransportDelegate.prepareFromMediaId(mediaId, extras);
229         }
230 
231         /** Wrapper for MediaController.TransportControls.prepareFromSearch() */
prepareFromSearch(String query, Bundle extras)232         public void prepareFromSearch(String query, Bundle extras) {
233             mTransportDelegate.prepareFromSearch(query, extras);
234         }
235 
236         /** Wrapper for MediaController.TransportControls.prepareFromUri() */
prepareFromUri(Uri uri, Bundle extras)237         public void prepareFromUri(Uri uri, Bundle extras) {
238             mTransportDelegate.prepareFromUri(uri, extras);
239         }
240 
241         /** Wrapper for MediaController.TransportControls.play() */
play()242         public void play() {
243             mTransportDelegate.play();
244         }
245 
246         /** Wrapper for MediaController.TransportControls.playFromMediaId() */
playFromMediaId(String mediaId, Bundle extras)247         public void playFromMediaId(String mediaId, Bundle extras) {
248             mTransportDelegate.playFromMediaId(mediaId, extras);
249         }
250 
251         /** Wrapper for MediaController.TransportControls.playFromSearch() */
playFromSearch(String query, Bundle extras)252         public void playFromSearch(String query, Bundle extras) {
253             mTransportDelegate.playFromSearch(query, extras);
254         }
255 
256         /** Wrapper for MediaController.TransportControls.playFromUri() */
playFromUri(Uri uri, Bundle extras)257         public void playFromUri(Uri uri, Bundle extras) {
258             mTransportDelegate.playFromUri(uri, extras);
259         }
260 
261         /** Wrapper for MediaController.TransportControls.skipToQueueItem() */
skipToQueueItem(long id)262         public void skipToQueueItem(long id) {
263             mTransportDelegate.skipToQueueItem(id);
264         }
265 
266         /** Wrapper for MediaController.TransportControls.pause() */
pause()267         public void pause() {
268             mTransportDelegate.pause();
269         }
270 
271         /** Wrapper for MediaController.TransportControls.stop() */
stop()272         public void stop() {
273             mTransportDelegate.stop();
274         }
275 
276         /** Wrapper for MediaController.TransportControls.seekTo() */
seekTo(long pos)277         public void seekTo(long pos) {
278             mTransportDelegate.seekTo(pos);
279         }
280 
281         /** Wrapper for MediaController.TransportControls.fastForward() */
fastForward()282         public void fastForward() {
283             mTransportDelegate.fastForward();
284         }
285 
286         /** Wrapper for MediaController.TransportControls.skipToNext() */
skipToNext()287         public void skipToNext() {
288             mTransportDelegate.skipToNext();
289         }
290 
291         /** Wrapper for MediaController.TransportControls.rewind() */
rewind()292         public void rewind() {
293             mTransportDelegate.rewind();
294         }
295 
296         /** Wrapper for MediaController.TransportControls.skipToPrevious() */
skipToPrevious()297         public void skipToPrevious() {
298             mTransportDelegate.skipToPrevious();
299         }
300 
301         /** Wrapper for MediaController.TransportControls.setRating() */
setRating(Rating rating)302         public void setRating(Rating rating) {
303             mTransportDelegate.setRating(rating);
304         }
305 
306         /** Wrapper for MediaController.TransportControls.setPlaybackSpeed(float speed) */
setPlaybackSpeed(float speed)307         public void setPlaybackSpeed(float speed) {
308             mTransportDelegate.setPlaybackSpeed(speed);
309         }
310 
311         /** Wrapper for MediaController.TransportControls.sendCustomAction() */
sendCustomAction( @onNull PlaybackState.CustomAction customAction, @Nullable Bundle args)312         public void sendCustomAction(
313                 @NonNull PlaybackState.CustomAction customAction, @Nullable Bundle args) {
314             mTransportDelegate.sendCustomAction(customAction, args);
315         }
316 
317         /** Wrapper for MediaController.TransportControls.sendCustomAction() */
sendCustomAction(@onNull String action, @Nullable Bundle args)318         public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
319             mTransportDelegate.sendCustomAction(action, args);
320         }
321     }
322 }
323