Loading android/app/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java +12 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import android.os.Looper; import android.os.Message; import android.util.Log; import com.android.bluetooth.Utils; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; import java.util.List; Loading @@ -47,6 +50,7 @@ public class BrowsablePlayerConnector { private static final int MSG_CONNECT_CB = 1; private static final int MSG_TIMEOUT = 2; private static BrowsablePlayerConnector sInjectConnector; private Handler mHandler; private Context mContext; private PlayerListCallback mCallback; Loading @@ -58,11 +62,19 @@ public class BrowsablePlayerConnector { void run(List<BrowsedPlayerWrapper> result); } private static void setInstanceForTesting(BrowsablePlayerConnector connector) { Utils.enforceInstrumentationTestMode(); sInjectConnector = connector; } static BrowsablePlayerConnector connectToPlayers( Context context, Looper looper, List<ResolveInfo> players, PlayerListCallback cb) { if (sInjectConnector != null) { return sInjectConnector; } if (cb == null) { Log.wtfStack(TAG, "Null callback passed"); return null; Loading android/app/src/com/android/bluetooth/avrcp/GPMWrapper.java +5 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.bluetooth.avrcp; import android.media.session.MediaSession; import android.os.Looper; import android.util.Log; /** Loading @@ -28,6 +29,10 @@ class GPMWrapper extends MediaPlayerWrapper { private static final String TAG = "AvrcpGPMWrapper"; private static final boolean DEBUG = true; GPMWrapper(MediaController controller, Looper looper) { super(controller, looper); } @Override boolean isMetadataSynced() { if (getQueue() == null) { Loading android/app/src/com/android/bluetooth/avrcp/MediaPlayerList.java +121 −11 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; Loading @@ -33,6 +35,7 @@ import android.util.Log; import android.view.KeyEvent; import com.android.bluetooth.Utils; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Collections; Loading Loading @@ -79,6 +82,8 @@ public class MediaPlayerList { private Looper mLooper; // Thread all media player callbacks and timeouts happen on private PackageManager mPackageManager; private MediaSessionManager mMediaSessionManager; private MediaData mCurrMediaData = null; private final AudioManager mAudioManager; private Map<Integer, MediaPlayerWrapper> mMediaPlayers = Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>()); Loading @@ -88,6 +93,9 @@ public class MediaPlayerList { Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>()); private int mActivePlayerId = NO_ACTIVE_PLAYER; @VisibleForTesting private boolean mAudioPlaybackIsActive = false; private AvrcpTargetService.ListCallback mCallback; private BrowsablePlayerConnector mBrowsablePlayerConnector; Loading Loading @@ -122,6 +130,9 @@ public class MediaPlayerList { pkgFilter.addDataScheme(PACKAGE_SCHEME); context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioManager.registerAudioPlaybackCallback(mAudioPlaybackCallback, new Handler(mLooper)); mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); mMediaSessionManager.addOnActiveSessionsChangedListener( Loading Loading @@ -190,6 +201,8 @@ public class MediaPlayerList { mMediaSessionManager.setCallback(null, null); mMediaSessionManager = null; mAudioManager.unregisterAudioPlaybackCallback(mAudioPlaybackCallback); mMediaPlayerIds.clear(); for (MediaPlayerWrapper player : mMediaPlayers.values()) { Loading Loading @@ -275,7 +288,16 @@ public class MediaPlayerList { final MediaPlayerWrapper player = getActivePlayer(); if (player == null) return null; return player.getPlaybackState(); PlaybackState state = player.getPlaybackState(); if (mAudioPlaybackIsActive && (state == null || state.getState() != PlaybackState.STATE_PLAYING)) { return new PlaybackState.Builder() .setState(PlaybackState.STATE_PLAYING, state == null ? 0 : state.getPosition(), 1.0f) .build(); } return state; } @NonNull Loading Loading @@ -406,12 +428,8 @@ public class MediaPlayerList { } } // Adds the controller to the MediaPlayerList or updates the controller if we already had // a controller for a package. Returns the new ID of the controller where its added or its // previous value if it already existed. Returns -1 if the controller passed in is invalid int addMediaPlayer(android.media.session.MediaController controller) { if (controller == null) return -1; @VisibleForTesting int addMediaPlayer(MediaController controller) { // Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that // there is no active player. If we already have a browsable player for the package, reuse // that key. Loading @@ -427,7 +445,7 @@ public class MediaPlayerList { if (mMediaPlayers.containsKey(playerId)) { d("Already have a controller for the player: " + packageName + ", updating instead"); MediaPlayerWrapper player = mMediaPlayers.get(playerId); player.updateMediaController(MediaControllerFactory.wrap(controller)); player.updateMediaController(controller); // If the media controller we updated was the active player check if the media updated if (playerId == mActivePlayerId) { Loading @@ -437,8 +455,8 @@ public class MediaPlayerList { return playerId; } MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap( MediaControllerFactory.wrap(controller), MediaPlayerWrapper newPlayer = MediaPlayerWrapperFactory.wrap( controller, mLooper); Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: " Loading @@ -448,6 +466,18 @@ public class MediaPlayerList { return playerId; } // Adds the controller to the MediaPlayerList or updates the controller if we already had // a controller for a package. Returns the new ID of the controller where its added or its // previous value if it already existed. Returns -1 if the controller passed in is invalid int addMediaPlayer(android.media.session.MediaController controller) { if (controller == null) { e("Trying to add a null MediaController"); return -1; } return addMediaPlayer(MediaControllerFactory.wrap(controller)); } void removeMediaPlayer(int playerId) { if (!mMediaPlayers.containsKey(playerId)) { e("Trying to remove nonexistent media player: " + playerId); Loading Loading @@ -504,7 +534,12 @@ public class MediaPlayerList { sendFolderUpdate(true, true, false); } sendMediaUpdate(getActivePlayer().getCurrentMediaData()); MediaData data = getActivePlayer().getCurrentMediaData(); if (mAudioPlaybackIsActive) { data.state = mCurrMediaData.state; Log.d(TAG, "setActivePlayer mAudioPlaybackIsActive=true, state=" + data.state); } sendMediaUpdate(data); } // TODO (apanicke): Add logging for media key events in dumpsys Loading Loading @@ -537,6 +572,8 @@ public class MediaPlayerList { data.queue.add(data.metadata); } Log.d(TAG, "sendMediaUpdate state=" + data.state); mCurrMediaData = data; mCallback.run(data); } Loading Loading @@ -600,6 +637,75 @@ public class MediaPlayerList { } }; void updateMediaForAudioPlayback() { MediaData currMediaData = null; PlaybackState currState = null; if (getActivePlayer() == null) { Log.d(TAG, "updateMediaForAudioPlayback: no active player"); PlaybackState.Builder builder = new PlaybackState.Builder() .setState(PlaybackState.STATE_STOPPED, 0L, 0L); List<Metadata> queue = new ArrayList<Metadata>(); queue.add(Util.empty_data()); currMediaData = new MediaData( Util.empty_data(), builder.build(), queue ); } else { currMediaData = getActivePlayer().getCurrentMediaData(); currState = currMediaData.state; } if (currState != null && currState.getState() == PlaybackState.STATE_PLAYING) { Log.i(TAG, "updateMediaForAudioPlayback: Active player is playing, drop it"); return; } if (mAudioPlaybackIsActive) { PlaybackState.Builder builder = new PlaybackState.Builder() .setState(PlaybackState.STATE_PLAYING, currState == null ? 0 : currState.getPosition(), 1.0f); currMediaData.state = builder.build(); } Log.i(TAG, "updateMediaForAudioPlayback: update state=" + currMediaData.state); sendMediaUpdate(currMediaData); } @VisibleForTesting void injectAudioPlaybacActive(boolean isActive) { mAudioPlaybackIsActive = isActive; updateMediaForAudioPlayback(); } private final AudioManager.AudioPlaybackCallback mAudioPlaybackCallback = new AudioManager.AudioPlaybackCallback() { @Override public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) { if (configs == null) { return; } boolean isActive = false; Log.v(TAG, "onPlaybackConfigChanged(): Configs list size=" + configs.size()); for (AudioPlaybackConfiguration config : configs) { if (config.isActive()) { if (DEBUG) { Log.d(TAG, "onPlaybackConfigChanged(): config=" + AudioPlaybackConfiguration.toLogFriendlyString(config)); } isActive = true; } } if (isActive != mAudioPlaybackIsActive) { Log.d(TAG, "onPlaybackConfigChanged isActive=" + isActive + ", mAudioPlaybackIsActive=" + mAudioPlaybackIsActive); mAudioPlaybackIsActive = isActive; updateMediaForAudioPlayback(); } } }; private final MediaPlayerWrapper.Callback mMediaPlayerCallback = new MediaPlayerWrapper.Callback() { @Override Loading @@ -614,6 +720,10 @@ public class MediaPlayerList { return; } if (mAudioPlaybackIsActive && (data.state.getState() != PlaybackState.STATE_PLAYING)) { Log.d(TAG, "Some audio playbacks are still active, drop it"); return; } sendMediaUpdate(data); } Loading android/app/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java +8 −27 Original line number Diff line number Diff line Loading @@ -54,10 +54,6 @@ class MediaPlayerWrapper { private final Object mCallbackLock = new Object(); private Callback mRegisteredCallback = null; protected MediaPlayerWrapper() { mCurrentData = new MediaData(null, null, null); } public interface Callback { void mediaUpdatedCallback(MediaData data); void sessionUpdatedCallback(String packageName); Loading @@ -81,30 +77,15 @@ class MediaPlayerWrapper { return true; } // TODO (apanicke): Implement a factory to make testing and creating interop wrappers easier static MediaPlayerWrapper wrap(MediaController controller, Looper looper) { if (controller == null || looper == null) { e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller + " | Looper: " + looper); return null; } MediaPlayerWrapper(MediaController controller, Looper looper) { mMediaController = controller; mPackageName = controller.getPackageName(); mLooper = looper; MediaPlayerWrapper newWrapper; if (controller.getPackageName().equals("com.google.android.music")) { Log.v(TAG, "Creating compatibility wrapper for Google Play Music"); newWrapper = new GPMWrapper(); } else { newWrapper = new MediaPlayerWrapper(); } newWrapper.mMediaController = controller; newWrapper.mPackageName = controller.getPackageName(); newWrapper.mLooper = looper; newWrapper.mCurrentData.queue = Util.toMetadataList(newWrapper.getQueue()); newWrapper.mCurrentData.metadata = Util.toMetadata(newWrapper.getMetadata()); newWrapper.mCurrentData.state = newWrapper.getPlaybackState(); return newWrapper; mCurrentData = new MediaData(null, null, null); mCurrentData.queue = Util.toMetadataList(getQueue()); mCurrentData.metadata = Util.toMetadata(getMetadata()); mCurrentData.state = getPlaybackState(); } void cleanup() { Loading android/app/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.avrcp; import android.os.Looper; import com.android.internal.annotations.VisibleForTesting; /** * Provide a method to inject custom MediaControllerWrapper objects for testing. By using the * factory methods instead of calling the wrap method of MediaControllerWrapper directly, we can * inject a custom MediaControllerWrapper that can be used with JUnit and Mockito to set * expectations and validate behaviour in tests. */ public final class MediaPlayerWrapperFactory { private static MediaPlayerWrapper sInjectedWrapper; static MediaPlayerWrapper wrap(MediaController controller, Looper looper) { if (sInjectedWrapper != null) return sInjectedWrapper; if (controller == null || looper == null) { return null; } MediaPlayerWrapper newWrapper; if (controller.getPackageName().equals("com.google.android.music")) { newWrapper = new GPMWrapper(controller, looper); } else { newWrapper = new MediaPlayerWrapper(controller, looper); } return newWrapper; } @VisibleForTesting static void inject(MediaPlayerWrapper wrapper) { sInjectedWrapper = wrapper; } } Loading
android/app/src/com/android/bluetooth/avrcp/BrowsablePlayerConnector.java +12 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import android.os.Looper; import android.os.Message; import android.util.Log; import com.android.bluetooth.Utils; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; import java.util.List; Loading @@ -47,6 +50,7 @@ public class BrowsablePlayerConnector { private static final int MSG_CONNECT_CB = 1; private static final int MSG_TIMEOUT = 2; private static BrowsablePlayerConnector sInjectConnector; private Handler mHandler; private Context mContext; private PlayerListCallback mCallback; Loading @@ -58,11 +62,19 @@ public class BrowsablePlayerConnector { void run(List<BrowsedPlayerWrapper> result); } private static void setInstanceForTesting(BrowsablePlayerConnector connector) { Utils.enforceInstrumentationTestMode(); sInjectConnector = connector; } static BrowsablePlayerConnector connectToPlayers( Context context, Looper looper, List<ResolveInfo> players, PlayerListCallback cb) { if (sInjectConnector != null) { return sInjectConnector; } if (cb == null) { Log.wtfStack(TAG, "Null callback passed"); return null; Loading
android/app/src/com/android/bluetooth/avrcp/GPMWrapper.java +5 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.bluetooth.avrcp; import android.media.session.MediaSession; import android.os.Looper; import android.util.Log; /** Loading @@ -28,6 +29,10 @@ class GPMWrapper extends MediaPlayerWrapper { private static final String TAG = "AvrcpGPMWrapper"; private static final boolean DEBUG = true; GPMWrapper(MediaController controller, Looper looper) { super(controller, looper); } @Override boolean isMetadataSynced() { if (getQueue() == null) { Loading
android/app/src/com/android/bluetooth/avrcp/MediaPlayerList.java +121 −11 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; Loading @@ -33,6 +35,7 @@ import android.util.Log; import android.view.KeyEvent; import com.android.bluetooth.Utils; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Collections; Loading Loading @@ -79,6 +82,8 @@ public class MediaPlayerList { private Looper mLooper; // Thread all media player callbacks and timeouts happen on private PackageManager mPackageManager; private MediaSessionManager mMediaSessionManager; private MediaData mCurrMediaData = null; private final AudioManager mAudioManager; private Map<Integer, MediaPlayerWrapper> mMediaPlayers = Collections.synchronizedMap(new HashMap<Integer, MediaPlayerWrapper>()); Loading @@ -88,6 +93,9 @@ public class MediaPlayerList { Collections.synchronizedMap(new HashMap<Integer, BrowsedPlayerWrapper>()); private int mActivePlayerId = NO_ACTIVE_PLAYER; @VisibleForTesting private boolean mAudioPlaybackIsActive = false; private AvrcpTargetService.ListCallback mCallback; private BrowsablePlayerConnector mBrowsablePlayerConnector; Loading Loading @@ -122,6 +130,9 @@ public class MediaPlayerList { pkgFilter.addDataScheme(PACKAGE_SCHEME); context.registerReceiver(mPackageChangedBroadcastReceiver, pkgFilter); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mAudioManager.registerAudioPlaybackCallback(mAudioPlaybackCallback, new Handler(mLooper)); mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE); mMediaSessionManager.addOnActiveSessionsChangedListener( Loading Loading @@ -190,6 +201,8 @@ public class MediaPlayerList { mMediaSessionManager.setCallback(null, null); mMediaSessionManager = null; mAudioManager.unregisterAudioPlaybackCallback(mAudioPlaybackCallback); mMediaPlayerIds.clear(); for (MediaPlayerWrapper player : mMediaPlayers.values()) { Loading Loading @@ -275,7 +288,16 @@ public class MediaPlayerList { final MediaPlayerWrapper player = getActivePlayer(); if (player == null) return null; return player.getPlaybackState(); PlaybackState state = player.getPlaybackState(); if (mAudioPlaybackIsActive && (state == null || state.getState() != PlaybackState.STATE_PLAYING)) { return new PlaybackState.Builder() .setState(PlaybackState.STATE_PLAYING, state == null ? 0 : state.getPosition(), 1.0f) .build(); } return state; } @NonNull Loading Loading @@ -406,12 +428,8 @@ public class MediaPlayerList { } } // Adds the controller to the MediaPlayerList or updates the controller if we already had // a controller for a package. Returns the new ID of the controller where its added or its // previous value if it already existed. Returns -1 if the controller passed in is invalid int addMediaPlayer(android.media.session.MediaController controller) { if (controller == null) return -1; @VisibleForTesting int addMediaPlayer(MediaController controller) { // Each new player has an ID of 1 plus the highest ID. The ID 0 is reserved to signify that // there is no active player. If we already have a browsable player for the package, reuse // that key. Loading @@ -427,7 +445,7 @@ public class MediaPlayerList { if (mMediaPlayers.containsKey(playerId)) { d("Already have a controller for the player: " + packageName + ", updating instead"); MediaPlayerWrapper player = mMediaPlayers.get(playerId); player.updateMediaController(MediaControllerFactory.wrap(controller)); player.updateMediaController(controller); // If the media controller we updated was the active player check if the media updated if (playerId == mActivePlayerId) { Loading @@ -437,8 +455,8 @@ public class MediaPlayerList { return playerId; } MediaPlayerWrapper newPlayer = MediaPlayerWrapper.wrap( MediaControllerFactory.wrap(controller), MediaPlayerWrapper newPlayer = MediaPlayerWrapperFactory.wrap( controller, mLooper); Log.i(TAG, "Adding wrapped media player: " + packageName + " at key: " Loading @@ -448,6 +466,18 @@ public class MediaPlayerList { return playerId; } // Adds the controller to the MediaPlayerList or updates the controller if we already had // a controller for a package. Returns the new ID of the controller where its added or its // previous value if it already existed. Returns -1 if the controller passed in is invalid int addMediaPlayer(android.media.session.MediaController controller) { if (controller == null) { e("Trying to add a null MediaController"); return -1; } return addMediaPlayer(MediaControllerFactory.wrap(controller)); } void removeMediaPlayer(int playerId) { if (!mMediaPlayers.containsKey(playerId)) { e("Trying to remove nonexistent media player: " + playerId); Loading Loading @@ -504,7 +534,12 @@ public class MediaPlayerList { sendFolderUpdate(true, true, false); } sendMediaUpdate(getActivePlayer().getCurrentMediaData()); MediaData data = getActivePlayer().getCurrentMediaData(); if (mAudioPlaybackIsActive) { data.state = mCurrMediaData.state; Log.d(TAG, "setActivePlayer mAudioPlaybackIsActive=true, state=" + data.state); } sendMediaUpdate(data); } // TODO (apanicke): Add logging for media key events in dumpsys Loading Loading @@ -537,6 +572,8 @@ public class MediaPlayerList { data.queue.add(data.metadata); } Log.d(TAG, "sendMediaUpdate state=" + data.state); mCurrMediaData = data; mCallback.run(data); } Loading Loading @@ -600,6 +637,75 @@ public class MediaPlayerList { } }; void updateMediaForAudioPlayback() { MediaData currMediaData = null; PlaybackState currState = null; if (getActivePlayer() == null) { Log.d(TAG, "updateMediaForAudioPlayback: no active player"); PlaybackState.Builder builder = new PlaybackState.Builder() .setState(PlaybackState.STATE_STOPPED, 0L, 0L); List<Metadata> queue = new ArrayList<Metadata>(); queue.add(Util.empty_data()); currMediaData = new MediaData( Util.empty_data(), builder.build(), queue ); } else { currMediaData = getActivePlayer().getCurrentMediaData(); currState = currMediaData.state; } if (currState != null && currState.getState() == PlaybackState.STATE_PLAYING) { Log.i(TAG, "updateMediaForAudioPlayback: Active player is playing, drop it"); return; } if (mAudioPlaybackIsActive) { PlaybackState.Builder builder = new PlaybackState.Builder() .setState(PlaybackState.STATE_PLAYING, currState == null ? 0 : currState.getPosition(), 1.0f); currMediaData.state = builder.build(); } Log.i(TAG, "updateMediaForAudioPlayback: update state=" + currMediaData.state); sendMediaUpdate(currMediaData); } @VisibleForTesting void injectAudioPlaybacActive(boolean isActive) { mAudioPlaybackIsActive = isActive; updateMediaForAudioPlayback(); } private final AudioManager.AudioPlaybackCallback mAudioPlaybackCallback = new AudioManager.AudioPlaybackCallback() { @Override public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) { if (configs == null) { return; } boolean isActive = false; Log.v(TAG, "onPlaybackConfigChanged(): Configs list size=" + configs.size()); for (AudioPlaybackConfiguration config : configs) { if (config.isActive()) { if (DEBUG) { Log.d(TAG, "onPlaybackConfigChanged(): config=" + AudioPlaybackConfiguration.toLogFriendlyString(config)); } isActive = true; } } if (isActive != mAudioPlaybackIsActive) { Log.d(TAG, "onPlaybackConfigChanged isActive=" + isActive + ", mAudioPlaybackIsActive=" + mAudioPlaybackIsActive); mAudioPlaybackIsActive = isActive; updateMediaForAudioPlayback(); } } }; private final MediaPlayerWrapper.Callback mMediaPlayerCallback = new MediaPlayerWrapper.Callback() { @Override Loading @@ -614,6 +720,10 @@ public class MediaPlayerList { return; } if (mAudioPlaybackIsActive && (data.state.getState() != PlaybackState.STATE_PLAYING)) { Log.d(TAG, "Some audio playbacks are still active, drop it"); return; } sendMediaUpdate(data); } Loading
android/app/src/com/android/bluetooth/avrcp/MediaPlayerWrapper.java +8 −27 Original line number Diff line number Diff line Loading @@ -54,10 +54,6 @@ class MediaPlayerWrapper { private final Object mCallbackLock = new Object(); private Callback mRegisteredCallback = null; protected MediaPlayerWrapper() { mCurrentData = new MediaData(null, null, null); } public interface Callback { void mediaUpdatedCallback(MediaData data); void sessionUpdatedCallback(String packageName); Loading @@ -81,30 +77,15 @@ class MediaPlayerWrapper { return true; } // TODO (apanicke): Implement a factory to make testing and creating interop wrappers easier static MediaPlayerWrapper wrap(MediaController controller, Looper looper) { if (controller == null || looper == null) { e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller + " | Looper: " + looper); return null; } MediaPlayerWrapper(MediaController controller, Looper looper) { mMediaController = controller; mPackageName = controller.getPackageName(); mLooper = looper; MediaPlayerWrapper newWrapper; if (controller.getPackageName().equals("com.google.android.music")) { Log.v(TAG, "Creating compatibility wrapper for Google Play Music"); newWrapper = new GPMWrapper(); } else { newWrapper = new MediaPlayerWrapper(); } newWrapper.mMediaController = controller; newWrapper.mPackageName = controller.getPackageName(); newWrapper.mLooper = looper; newWrapper.mCurrentData.queue = Util.toMetadataList(newWrapper.getQueue()); newWrapper.mCurrentData.metadata = Util.toMetadata(newWrapper.getMetadata()); newWrapper.mCurrentData.state = newWrapper.getPlaybackState(); return newWrapper; mCurrentData = new MediaData(null, null, null); mCurrentData.queue = Util.toMetadataList(getQueue()); mCurrentData.metadata = Util.toMetadata(getMetadata()); mCurrentData.state = getPlaybackState(); } void cleanup() { Loading
android/app/src/com/android/bluetooth/avrcp/mockable/MediaPlayerWrapperFactory.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.avrcp; import android.os.Looper; import com.android.internal.annotations.VisibleForTesting; /** * Provide a method to inject custom MediaControllerWrapper objects for testing. By using the * factory methods instead of calling the wrap method of MediaControllerWrapper directly, we can * inject a custom MediaControllerWrapper that can be used with JUnit and Mockito to set * expectations and validate behaviour in tests. */ public final class MediaPlayerWrapperFactory { private static MediaPlayerWrapper sInjectedWrapper; static MediaPlayerWrapper wrap(MediaController controller, Looper looper) { if (sInjectedWrapper != null) return sInjectedWrapper; if (controller == null || looper == null) { return null; } MediaPlayerWrapper newWrapper; if (controller.getPackageName().equals("com.google.android.music")) { newWrapper = new GPMWrapper(controller, looper); } else { newWrapper = new MediaPlayerWrapper(controller, looper); } return newWrapper; } @VisibleForTesting static void inject(MediaPlayerWrapper wrapper) { sInjectedWrapper = wrapper; } }