Loading android/app/src/com/android/bluetooth/newavrcp/GPMWrapper.java +0 −6 Original line number Original line Diff line number Diff line Loading @@ -31,12 +31,6 @@ class GPMWrapper extends MediaPlayerWrapper { private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata"; private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata"; // Google Play Music should always be browsable. @Override boolean isBrowsable() { return true; } @Override @Override boolean isMetadataSynced() { boolean isMetadataSynced() { // Check if currentPlayingQueueId is in the queue // Check if currentPlayingQueueId is in the queue Loading android/app/src/com/android/bluetooth/newavrcp/MediaPlayerWrapper.java +78 −55 Original line number Original line Diff line number Diff line /* /* * Copyright 2017 The Android Open Source Project * Copyright 2018 The Android Open Source Project * * * Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading Loading @@ -29,11 +29,13 @@ import android.support.annotation.VisibleForTesting; import android.util.Log; import android.util.Log; import java.util.List; import java.util.List; import java.util.Objects; /* /* * A class to synchronize Media Controller Callbacks and only pass through * A class to synchronize Media Controller Callbacks and only pass through * an update once all the relevant information is current. * an update once all the relevant information is current. * * TODO (apanicke): Once MediaPlayer2 is supported better, replace this class * with that. */ */ class MediaPlayerWrapper { class MediaPlayerWrapper { private static final String TAG = "NewAvrcpMediaPlayerWrapper"; private static final String TAG = "NewAvrcpMediaPlayerWrapper"; Loading @@ -44,7 +46,6 @@ class MediaPlayerWrapper { private String mPackageName; private String mPackageName; private Looper mLooper; private Looper mLooper; private boolean mIsBrowsable = false; private MediaData mCurrentData; private MediaData mCurrentData; @GuardedBy("mCallbackLock") @GuardedBy("mCallbackLock") Loading @@ -57,45 +58,26 @@ class MediaPlayerWrapper { mCurrentData = new MediaData(null, null, null); mCurrentData = new MediaData(null, null, null); } } interface Callback { public interface Callback { void mediaUpdatedCallback(MediaData data); void mediaUpdatedCallback(MediaData data); } } class MediaData { boolean isReady() { public List<MediaSession.QueueItem> queue; if (getPlaybackState() == null) { public PlaybackState state; d("isReady(): PlaybackState is null"); public MediaMetadata metadata; MediaData(MediaMetadata m, PlaybackState s, List<MediaSession.QueueItem> q) { metadata = m; state = s; queue = q; } @Override public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof MediaData)) return false; final MediaData u = (MediaData) o; if (!Objects.equals(metadata, u.metadata)) { return false; } if (!Objects.equals(queue, u.queue)) { return false; return false; } } if (!playstateEquals(state, u.state)) { if (getMetadata() == null) { d("isReady(): Metadata is null"); return false; return false; } } return true; return true; } } } static MediaPlayerWrapper wrap(MediaController controller, Looper looper, boolean browsable) { // 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) { if (controller == null || looper == null) { e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller + " | Looper: " + looper); + " | Looper: " + looper); Loading @@ -113,12 +95,10 @@ class MediaPlayerWrapper { newWrapper.mMediaController = controller; newWrapper.mMediaController = controller; newWrapper.mPackageName = controller.getPackageName(); newWrapper.mPackageName = controller.getPackageName(); newWrapper.mLooper = looper; newWrapper.mLooper = looper; newWrapper.mIsBrowsable = browsable; newWrapper.mCurrentData.queue = newWrapper.getQueue(); newWrapper.mCurrentData.queue = newWrapper.getQueue(); newWrapper.mCurrentData.metadata = newWrapper.getMetadata(); newWrapper.mCurrentData.metadata = newWrapper.getMetadata(); newWrapper.mCurrentData.state = newWrapper.getPlaybackState(); newWrapper.mCurrentData.state = newWrapper.getPlaybackState(); return newWrapper; return newWrapper; } } Loading @@ -129,17 +109,11 @@ class MediaPlayerWrapper { mLooper = null; mLooper = null; } } boolean isBrowsable() { return mIsBrowsable; } String getPackageName() { String getPackageName() { return mPackageName; return mPackageName; } } List<MediaSession.QueueItem> getQueue() { List<MediaSession.QueueItem> getQueue() { if (!isBrowsable()) return null; return mMediaController.getQueue(); return mMediaController.getQueue(); } } Loading @@ -156,7 +130,23 @@ class MediaPlayerWrapper { return mMediaController.getPlaybackState(); return mMediaController.getPlaybackState(); } } // TODO: Implement shuffle and repeat support. Right now these use custom actions MediaData getCurrentMediaData() { return mCurrentData; } void playItemFromQueue(long qid) { // Return immediately if no queue exists. if (getQueue() == null) { Log.w(TAG, "playItemFromQueue: Trying to play item for player that has no queue: " + mPackageName); return; } MediaController.TransportControls controller = mMediaController.getTransportControls(); controller.skipToQueueItem(qid); } // TODO (apanicke): Implement shuffle and repeat support. Right now these use custom actions // and it may only be possible to do this with Google Play Music // and it may only be possible to do this with Google Play Music boolean isShuffleSupported() { boolean isShuffleSupported() { return false; return false; Loading @@ -175,12 +165,10 @@ class MediaPlayerWrapper { } } /** /** * Return whether the queue, metadata, and queueID are all in sync. If * Return whether the queue, metadata, and queueID are all in sync. * browsing isn't supported we don't have to worry about the queue as * the queue doesn't exist */ */ boolean isMetadataSynced() { boolean isMetadataSynced() { if (isBrowsable()) { if (getQueue() != null) { // Check if currentPlayingQueueId is in the current Queue // Check if currentPlayingQueueId is in the current Queue MediaSession.QueueItem currItem = null; MediaSession.QueueItem currItem = null; Loading Loading @@ -237,6 +225,21 @@ class MediaPlayerWrapper { mControllerCallbacks = null; mControllerCallbacks = null; } } void updateMediaController(MediaController newController) { if (newController == mMediaController) return; synchronized (mCallbackLock) { if (mRegisteredCallback == null || mControllerCallbacks == null) { return; } } mControllerCallbacks.cleanup(); mMediaController = newController; mControllerCallbacks = new MediaControllerListener(mLooper); d("Controller for " + mPackageName + " was updated."); } class TimeoutHandler extends Handler { class TimeoutHandler extends Handler { private static final int MSG_TIMEOUT = 0; private static final int MSG_TIMEOUT = 0; private static final long CALLBACK_TIMEOUT_MS = 1000; private static final long CALLBACK_TIMEOUT_MS = 1000; Loading @@ -255,7 +258,7 @@ class MediaPlayerWrapper { Log.e(TAG, "Timeout while waiting for metadata to sync for " + mPackageName); Log.e(TAG, "Timeout while waiting for metadata to sync for " + mPackageName); Log.e(TAG, " └ Current Metadata: " + getMetadata().getDescription()); Log.e(TAG, " └ Current Metadata: " + getMetadata().getDescription()); Log.e(TAG, " └ Current Playstate: " + getPlaybackState()); Log.e(TAG, " └ Current Playstate: " + getPlaybackState()); for (int i = 0; i < getQueue().size(); i++) { for (int i = 0; getQueue() != null && i < getQueue().size(); i++) { Log.e(TAG, " └ QueueItem(" + i + "): " + getQueue().get(i)); Log.e(TAG, " └ QueueItem(" + i + "): " + getQueue().get(i)); } } Loading Loading @@ -293,10 +296,7 @@ class MediaPlayerWrapper { mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT); mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT); if (!isMetadataSynced()) { if (!isMetadataSynced()) { if (DEBUG) { d("trySendMediaUpdate(): Starting media update timeout"); Log.d(TAG, "trySendMediaUpdate(): " + mPackageName + ": Starting media update timeout"); } mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_TIMEOUT, mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_TIMEOUT, TimeoutHandler.CALLBACK_TIMEOUT_MS); TimeoutHandler.CALLBACK_TIMEOUT_MS); return; return; Loading Loading @@ -328,6 +328,11 @@ class MediaPlayerWrapper { @Override @Override public void onMetadataChanged(MediaMetadata metadata) { public void onMetadataChanged(MediaMetadata metadata) { if (!isReady()) { Log.v(TAG, mPackageName + " tried to update with incomplete metadata"); return; } Log.v(TAG, "onMetadataChanged(): " + mPackageName + " : " + metadata.getDescription()); Log.v(TAG, "onMetadataChanged(): " + mPackageName + " : " + metadata.getDescription()); if (!metadata.equals(getMetadata())) { if (!metadata.equals(getMetadata())) { Loading @@ -353,6 +358,11 @@ class MediaPlayerWrapper { @Override @Override public void onPlaybackStateChanged(PlaybackState state) { public void onPlaybackStateChanged(PlaybackState state) { if (!isReady()) { Log.v(TAG, mPackageName + " tried to update with no state"); return; } Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state.toString()); Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state.toString()); if (!playstateEquals(state, getPlaybackState())) { if (!playstateEquals(state, getPlaybackState())) { Loading @@ -377,10 +387,12 @@ class MediaPlayerWrapper { @Override @Override public void onQueueChanged(List<MediaSession.QueueItem> queue) { public void onQueueChanged(List<MediaSession.QueueItem> queue) { Log.v(TAG, "onQueueChanged(): " + mPackageName); Log.v(TAG, "onQueueChanged(): " + mPackageName); if (!isBrowsable()) { e("Queue changed for non-browsable player " + mPackageName); if (!isReady()) { Log.v(TAG, mPackageName + " tried to updated with no queue"); return; return; } } if (!queue.equals(getQueue())) { if (!queue.equals(getQueue())) { e("The callback queue isn't the current queue"); e("The callback queue isn't the current queue"); } } Loading @@ -400,7 +412,9 @@ class MediaPlayerWrapper { } } @Override @Override public void onSessionDestroyed() {} public void onSessionDestroyed() { Log.w(TAG, "The session was destroyed " + mPackageName); } @VisibleForTesting @VisibleForTesting Handler getTimeoutHandler() { Handler getTimeoutHandler() { Loading Loading @@ -430,6 +444,7 @@ class MediaPlayerWrapper { return false; return false; } } // TODO: Use this function when returning the now playing list /** /** * Extracts different pieces of metadata from a MediaSession.QueueItem * Extracts different pieces of metadata from a MediaSession.QueueItem * and builds a MediaMetadata Object out of it. * and builds a MediaMetadata Object out of it. Loading Loading @@ -475,9 +490,17 @@ class MediaPlayerWrapper { } } } } private void d(String message) { if (DEBUG) Log.d(TAG, mPackageName + ": " + message); } @VisibleForTesting @VisibleForTesting Handler getTimeoutHandler() { Handler getTimeoutHandler() { if (mControllerCallbacks == null) return null; if (mControllerCallbacks == null) return null; return mControllerCallbacks.getTimeoutHandler(); return mControllerCallbacks.getTimeoutHandler(); } } public void dump(StringBuilder sb) { sb.append(mMediaController.toString() + "\n"); } } } android/app/src/com/android/bluetooth/newavrcp/helpers/MediaData.java 0 → 100644 +61 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 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.media.MediaMetadata; import android.media.session.MediaSession; import android.media.session.PlaybackState; import java.util.List; import java.util.Objects; /* * Helper class to transport metadata around AVRCP */ class MediaData { public List<MediaSession.QueueItem> queue; public PlaybackState state; public MediaMetadata metadata; MediaData(MediaMetadata m, PlaybackState s, List<MediaSession.QueueItem> q) { metadata = m; state = s; queue = q; } @Override public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof MediaData)) return false; final MediaData u = (MediaData) o; if (!MediaPlayerWrapper.playstateEquals(state, u.state)) { return false; } if (!Objects.equals(metadata, u.metadata)) { return false; } if (!Objects.equals(queue, u.queue)) { return false; } return true; } } android/app/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java +58 −61 Original line number Original line Diff line number Diff line Loading @@ -54,7 +54,7 @@ public class MediaPlayerWrapperTest { private PlaybackState.Builder mTestState; private PlaybackState.Builder mTestState; @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs; @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs; @Captor ArgumentCaptor<MediaPlayerWrapper.MediaData> mMediaUpdateData; @Captor ArgumentCaptor<MediaData> mMediaUpdateData; @Mock Log.TerribleFailureHandler mFailHandler; @Mock Log.TerribleFailureHandler mFailHandler; @Mock MediaController mMockController; @Mock MediaController mMockController; @Mock MediaPlayerWrapper.Callback mTestCbs; @Mock MediaPlayerWrapper.Callback mTestCbs; Loading @@ -76,7 +76,7 @@ public class MediaPlayerWrapperTest { public void setUp() { public void setUp() { MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this); // Set failure handler to caputer Log.wtf messages // Set failure handler to capture Log.wtf messages Log.setWtfHandler(mFailHandler); Log.setWtfHandler(mFailHandler); // Set up Looper thread for the timeout handler // Set up Looper thread for the timeout handler Loading Loading @@ -127,7 +127,7 @@ public class MediaPlayerWrapperTest { doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); // Enable testing flag which enables Log.wtf statements. Some tests test against improper // Enable testing flag which enables Log.wtf statements. Some tests test against improper // behaviour and the TerribleFailureListener is a good way to ensure that the error occured // behaviour and the TerribleFailureListener is a good way to ensure that the error occurred MediaPlayerWrapper.sTesting = true; MediaPlayerWrapper.sTesting = true; } } Loading @@ -137,26 +137,55 @@ public class MediaPlayerWrapperTest { */ */ @Test @Test public void testNullControllerLooper() { public void testNullControllerLooper() { MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper(), false); MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper()); Assert.assertNull(wrapper); Assert.assertNull(wrapper); wrapper = MediaPlayerWrapper.wrap(mMockController, null, false); wrapper = MediaPlayerWrapper.wrap(mMockController, null); Assert.assertNull(wrapper); Assert.assertNull(wrapper); } } /* * Test to make sure that isReady() returns false if there is no playback state, * there is no metadata, or if the metadata has no title. */ @Test public void testIsReady() { MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); Assert.assertTrue(wrapper.isReady()); // Test isReady() is false when the playback state is null doReturn(null).when(mMockController).getPlaybackState(); Assert.assertFalse(wrapper.isReady()); // Restore the old playback state doReturn(mTestState.build()).when(mMockController).getPlaybackState(); Assert.assertTrue(wrapper.isReady()); // Test isReady() is false when the metadata is null doReturn(null).when(mMockController).getMetadata(); Assert.assertFalse(wrapper.isReady()); // Restore the old metadata doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); Assert.assertTrue(wrapper.isReady()); } /* /* * Test to make sure that a media player update gets sent whenever a Media metadata or playback * Test to make sure that a media player update gets sent whenever a Media metadata or playback * state change occurs instead of waiting for all data to be synced if the player doesn't * state change occurs instead of waiting for all data to be synced if the player doesn't * support browsing and queues. * support queues. */ */ @Test @Test public void testNoBrowsingMediaUpdates() { public void testNoQueueMediaUpdates() { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Return null when getting the queue doReturn(null).when(mMockController).getQueue(); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); Loading @@ -168,7 +197,7 @@ public class MediaPlayerWrapperTest { // Assert that the metadata was updated and playback state wasn't // Assert that the metadata was updated and playback state wasn't verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned Metadata isn't equal to given Metadata", "Returned Metadata isn't equal to given Metadata", data.metadata.getDescription(), data.metadata.getDescription(), Loading Loading @@ -202,58 +231,23 @@ public class MediaPlayerWrapperTest { verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } } /* * Test that trying to get the queue on a player that doesn't support * browsing returns false. */ @Test public void testNoBrowsingNullQueue() { // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); // Update Queue returned by controller mTestQueue.add( new MediaDescription.Builder() .setTitle("New Title") .setSubtitle("BT Test Artist") .setDescription("BT Test Album") .setMediaId("103")); doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); controllerCallbacks.onQueueChanged(getQueueFromDescriptions(mTestQueue)); // Verify no updates happened verify(mTestCbs, never()).mediaUpdatedCallback(any()); // Verify that getQueue() returns null Assert.assertNull(wrapper.getQueue()); // Verify that there was an error message pending and there were no timeouts Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); verify(mFailHandler, times(1)).onTerribleFailure(any(), any(), anyBoolean()); } /* /* * This test updates the metadata and playback state returned by the * This test updates the metadata and playback state returned by the * controller then sends an update. This is to make sure that all relevant * controller then sends an update. This is to make sure that all relevant * information is sent with every update. In the case without browsing, * information is sent with every update. In the case without a queue, * metadata and playback state are updated. * metadata and playback state are updated. */ */ @Test @Test public void testAllDataOnUpdate() { public void testDataOnUpdateNoQueue() { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Return null when getting the queue doReturn(null).when(mMockController).getQueue(); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); Loading @@ -271,7 +265,7 @@ public class MediaPlayerWrapperTest { // Assert that both metadata and playback state are there. // Assert that both metadata and playback state are there. verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned PlaybackState isn't equal to given PlaybackState", "Returned PlaybackState isn't equal to given PlaybackState", data.state.toString(), data.state.toString(), Loading @@ -288,7 +282,7 @@ public class MediaPlayerWrapperTest { } } /* /* * This test sends repeted Playback State updates that only have a short * This test sends repeated Playback State updates that only have a short * position update change to see if they get debounced. * position update change to see if they get debounced. */ */ @Test @Test Loading @@ -296,9 +290,12 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Return null when getting the queue doReturn(null).when(mMockController).getQueue(); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); Loading @@ -310,7 +307,7 @@ public class MediaPlayerWrapperTest { // Assert that both metadata and only the first playback state is there. // Assert that both metadata and only the first playback state is there. verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned PlaybackState isn't equal to given PlaybackState", "Returned PlaybackState isn't equal to given PlaybackState", data.state.toString(), data.state.toString(), Loading Loading @@ -350,7 +347,7 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Cleanup the wrapper // Cleanup the wrapper Loading @@ -370,7 +367,7 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -400,7 +397,7 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -430,7 +427,7 @@ public class MediaPlayerWrapperTest { // Assert that the callback was called with the updated data // Assert that the callback was called with the updated data verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned Metadata isn't equal to given Metadata", "Returned Metadata isn't equal to given Metadata", data.metadata.getDescription(), data.metadata.getDescription(), Loading Loading @@ -460,7 +457,7 @@ public class MediaPlayerWrapperTest { InstrumentationRegistry.getInstrumentation() InstrumentationRegistry.getInstrumentation() .acquireLooperManager(mThread.getLooper()); .acquireLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -494,7 +491,7 @@ public class MediaPlayerWrapperTest { InstrumentationRegistry.getInstrumentation() InstrumentationRegistry.getInstrumentation() .acquireLooperManager(mThread.getLooper()); .acquireLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -545,7 +542,7 @@ public class MediaPlayerWrapperTest { // Check that the callback was called a certain number of times and // Check that the callback was called a certain number of times and // that all the Media info matches what was given // that all the Media info matches what was given verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned Metadata isn't equal to given Metadata", "Returned Metadata isn't equal to given Metadata", data.metadata.getDescription(), data.metadata.getDescription(), Loading Loading
android/app/src/com/android/bluetooth/newavrcp/GPMWrapper.java +0 −6 Original line number Original line Diff line number Diff line Loading @@ -31,12 +31,6 @@ class GPMWrapper extends MediaPlayerWrapper { private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata"; private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata"; // Google Play Music should always be browsable. @Override boolean isBrowsable() { return true; } @Override @Override boolean isMetadataSynced() { boolean isMetadataSynced() { // Check if currentPlayingQueueId is in the queue // Check if currentPlayingQueueId is in the queue Loading
android/app/src/com/android/bluetooth/newavrcp/MediaPlayerWrapper.java +78 −55 Original line number Original line Diff line number Diff line /* /* * Copyright 2017 The Android Open Source Project * Copyright 2018 The Android Open Source Project * * * Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading Loading @@ -29,11 +29,13 @@ import android.support.annotation.VisibleForTesting; import android.util.Log; import android.util.Log; import java.util.List; import java.util.List; import java.util.Objects; /* /* * A class to synchronize Media Controller Callbacks and only pass through * A class to synchronize Media Controller Callbacks and only pass through * an update once all the relevant information is current. * an update once all the relevant information is current. * * TODO (apanicke): Once MediaPlayer2 is supported better, replace this class * with that. */ */ class MediaPlayerWrapper { class MediaPlayerWrapper { private static final String TAG = "NewAvrcpMediaPlayerWrapper"; private static final String TAG = "NewAvrcpMediaPlayerWrapper"; Loading @@ -44,7 +46,6 @@ class MediaPlayerWrapper { private String mPackageName; private String mPackageName; private Looper mLooper; private Looper mLooper; private boolean mIsBrowsable = false; private MediaData mCurrentData; private MediaData mCurrentData; @GuardedBy("mCallbackLock") @GuardedBy("mCallbackLock") Loading @@ -57,45 +58,26 @@ class MediaPlayerWrapper { mCurrentData = new MediaData(null, null, null); mCurrentData = new MediaData(null, null, null); } } interface Callback { public interface Callback { void mediaUpdatedCallback(MediaData data); void mediaUpdatedCallback(MediaData data); } } class MediaData { boolean isReady() { public List<MediaSession.QueueItem> queue; if (getPlaybackState() == null) { public PlaybackState state; d("isReady(): PlaybackState is null"); public MediaMetadata metadata; MediaData(MediaMetadata m, PlaybackState s, List<MediaSession.QueueItem> q) { metadata = m; state = s; queue = q; } @Override public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof MediaData)) return false; final MediaData u = (MediaData) o; if (!Objects.equals(metadata, u.metadata)) { return false; } if (!Objects.equals(queue, u.queue)) { return false; return false; } } if (!playstateEquals(state, u.state)) { if (getMetadata() == null) { d("isReady(): Metadata is null"); return false; return false; } } return true; return true; } } } static MediaPlayerWrapper wrap(MediaController controller, Looper looper, boolean browsable) { // 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) { if (controller == null || looper == null) { e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller e("MediaPlayerWrapper.wrap(): Null parameter - Controller: " + controller + " | Looper: " + looper); + " | Looper: " + looper); Loading @@ -113,12 +95,10 @@ class MediaPlayerWrapper { newWrapper.mMediaController = controller; newWrapper.mMediaController = controller; newWrapper.mPackageName = controller.getPackageName(); newWrapper.mPackageName = controller.getPackageName(); newWrapper.mLooper = looper; newWrapper.mLooper = looper; newWrapper.mIsBrowsable = browsable; newWrapper.mCurrentData.queue = newWrapper.getQueue(); newWrapper.mCurrentData.queue = newWrapper.getQueue(); newWrapper.mCurrentData.metadata = newWrapper.getMetadata(); newWrapper.mCurrentData.metadata = newWrapper.getMetadata(); newWrapper.mCurrentData.state = newWrapper.getPlaybackState(); newWrapper.mCurrentData.state = newWrapper.getPlaybackState(); return newWrapper; return newWrapper; } } Loading @@ -129,17 +109,11 @@ class MediaPlayerWrapper { mLooper = null; mLooper = null; } } boolean isBrowsable() { return mIsBrowsable; } String getPackageName() { String getPackageName() { return mPackageName; return mPackageName; } } List<MediaSession.QueueItem> getQueue() { List<MediaSession.QueueItem> getQueue() { if (!isBrowsable()) return null; return mMediaController.getQueue(); return mMediaController.getQueue(); } } Loading @@ -156,7 +130,23 @@ class MediaPlayerWrapper { return mMediaController.getPlaybackState(); return mMediaController.getPlaybackState(); } } // TODO: Implement shuffle and repeat support. Right now these use custom actions MediaData getCurrentMediaData() { return mCurrentData; } void playItemFromQueue(long qid) { // Return immediately if no queue exists. if (getQueue() == null) { Log.w(TAG, "playItemFromQueue: Trying to play item for player that has no queue: " + mPackageName); return; } MediaController.TransportControls controller = mMediaController.getTransportControls(); controller.skipToQueueItem(qid); } // TODO (apanicke): Implement shuffle and repeat support. Right now these use custom actions // and it may only be possible to do this with Google Play Music // and it may only be possible to do this with Google Play Music boolean isShuffleSupported() { boolean isShuffleSupported() { return false; return false; Loading @@ -175,12 +165,10 @@ class MediaPlayerWrapper { } } /** /** * Return whether the queue, metadata, and queueID are all in sync. If * Return whether the queue, metadata, and queueID are all in sync. * browsing isn't supported we don't have to worry about the queue as * the queue doesn't exist */ */ boolean isMetadataSynced() { boolean isMetadataSynced() { if (isBrowsable()) { if (getQueue() != null) { // Check if currentPlayingQueueId is in the current Queue // Check if currentPlayingQueueId is in the current Queue MediaSession.QueueItem currItem = null; MediaSession.QueueItem currItem = null; Loading Loading @@ -237,6 +225,21 @@ class MediaPlayerWrapper { mControllerCallbacks = null; mControllerCallbacks = null; } } void updateMediaController(MediaController newController) { if (newController == mMediaController) return; synchronized (mCallbackLock) { if (mRegisteredCallback == null || mControllerCallbacks == null) { return; } } mControllerCallbacks.cleanup(); mMediaController = newController; mControllerCallbacks = new MediaControllerListener(mLooper); d("Controller for " + mPackageName + " was updated."); } class TimeoutHandler extends Handler { class TimeoutHandler extends Handler { private static final int MSG_TIMEOUT = 0; private static final int MSG_TIMEOUT = 0; private static final long CALLBACK_TIMEOUT_MS = 1000; private static final long CALLBACK_TIMEOUT_MS = 1000; Loading @@ -255,7 +258,7 @@ class MediaPlayerWrapper { Log.e(TAG, "Timeout while waiting for metadata to sync for " + mPackageName); Log.e(TAG, "Timeout while waiting for metadata to sync for " + mPackageName); Log.e(TAG, " └ Current Metadata: " + getMetadata().getDescription()); Log.e(TAG, " └ Current Metadata: " + getMetadata().getDescription()); Log.e(TAG, " └ Current Playstate: " + getPlaybackState()); Log.e(TAG, " └ Current Playstate: " + getPlaybackState()); for (int i = 0; i < getQueue().size(); i++) { for (int i = 0; getQueue() != null && i < getQueue().size(); i++) { Log.e(TAG, " └ QueueItem(" + i + "): " + getQueue().get(i)); Log.e(TAG, " └ QueueItem(" + i + "): " + getQueue().get(i)); } } Loading Loading @@ -293,10 +296,7 @@ class MediaPlayerWrapper { mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT); mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT); if (!isMetadataSynced()) { if (!isMetadataSynced()) { if (DEBUG) { d("trySendMediaUpdate(): Starting media update timeout"); Log.d(TAG, "trySendMediaUpdate(): " + mPackageName + ": Starting media update timeout"); } mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_TIMEOUT, mTimeoutHandler.sendEmptyMessageDelayed(TimeoutHandler.MSG_TIMEOUT, TimeoutHandler.CALLBACK_TIMEOUT_MS); TimeoutHandler.CALLBACK_TIMEOUT_MS); return; return; Loading Loading @@ -328,6 +328,11 @@ class MediaPlayerWrapper { @Override @Override public void onMetadataChanged(MediaMetadata metadata) { public void onMetadataChanged(MediaMetadata metadata) { if (!isReady()) { Log.v(TAG, mPackageName + " tried to update with incomplete metadata"); return; } Log.v(TAG, "onMetadataChanged(): " + mPackageName + " : " + metadata.getDescription()); Log.v(TAG, "onMetadataChanged(): " + mPackageName + " : " + metadata.getDescription()); if (!metadata.equals(getMetadata())) { if (!metadata.equals(getMetadata())) { Loading @@ -353,6 +358,11 @@ class MediaPlayerWrapper { @Override @Override public void onPlaybackStateChanged(PlaybackState state) { public void onPlaybackStateChanged(PlaybackState state) { if (!isReady()) { Log.v(TAG, mPackageName + " tried to update with no state"); return; } Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state.toString()); Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state.toString()); if (!playstateEquals(state, getPlaybackState())) { if (!playstateEquals(state, getPlaybackState())) { Loading @@ -377,10 +387,12 @@ class MediaPlayerWrapper { @Override @Override public void onQueueChanged(List<MediaSession.QueueItem> queue) { public void onQueueChanged(List<MediaSession.QueueItem> queue) { Log.v(TAG, "onQueueChanged(): " + mPackageName); Log.v(TAG, "onQueueChanged(): " + mPackageName); if (!isBrowsable()) { e("Queue changed for non-browsable player " + mPackageName); if (!isReady()) { Log.v(TAG, mPackageName + " tried to updated with no queue"); return; return; } } if (!queue.equals(getQueue())) { if (!queue.equals(getQueue())) { e("The callback queue isn't the current queue"); e("The callback queue isn't the current queue"); } } Loading @@ -400,7 +412,9 @@ class MediaPlayerWrapper { } } @Override @Override public void onSessionDestroyed() {} public void onSessionDestroyed() { Log.w(TAG, "The session was destroyed " + mPackageName); } @VisibleForTesting @VisibleForTesting Handler getTimeoutHandler() { Handler getTimeoutHandler() { Loading Loading @@ -430,6 +444,7 @@ class MediaPlayerWrapper { return false; return false; } } // TODO: Use this function when returning the now playing list /** /** * Extracts different pieces of metadata from a MediaSession.QueueItem * Extracts different pieces of metadata from a MediaSession.QueueItem * and builds a MediaMetadata Object out of it. * and builds a MediaMetadata Object out of it. Loading Loading @@ -475,9 +490,17 @@ class MediaPlayerWrapper { } } } } private void d(String message) { if (DEBUG) Log.d(TAG, mPackageName + ": " + message); } @VisibleForTesting @VisibleForTesting Handler getTimeoutHandler() { Handler getTimeoutHandler() { if (mControllerCallbacks == null) return null; if (mControllerCallbacks == null) return null; return mControllerCallbacks.getTimeoutHandler(); return mControllerCallbacks.getTimeoutHandler(); } } public void dump(StringBuilder sb) { sb.append(mMediaController.toString() + "\n"); } } }
android/app/src/com/android/bluetooth/newavrcp/helpers/MediaData.java 0 → 100644 +61 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2018 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.media.MediaMetadata; import android.media.session.MediaSession; import android.media.session.PlaybackState; import java.util.List; import java.util.Objects; /* * Helper class to transport metadata around AVRCP */ class MediaData { public List<MediaSession.QueueItem> queue; public PlaybackState state; public MediaMetadata metadata; MediaData(MediaMetadata m, PlaybackState s, List<MediaSession.QueueItem> q) { metadata = m; state = s; queue = q; } @Override public boolean equals(Object o) { if (o == null) return false; if (!(o instanceof MediaData)) return false; final MediaData u = (MediaData) o; if (!MediaPlayerWrapper.playstateEquals(state, u.state)) { return false; } if (!Objects.equals(metadata, u.metadata)) { return false; } if (!Objects.equals(queue, u.queue)) { return false; } return true; } }
android/app/tests/unit/src/com/android/bluetooth/newavrcp/MediaPlayerWrapperTest.java +58 −61 Original line number Original line Diff line number Diff line Loading @@ -54,7 +54,7 @@ public class MediaPlayerWrapperTest { private PlaybackState.Builder mTestState; private PlaybackState.Builder mTestState; @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs; @Captor ArgumentCaptor<MediaController.Callback> mControllerCbs; @Captor ArgumentCaptor<MediaPlayerWrapper.MediaData> mMediaUpdateData; @Captor ArgumentCaptor<MediaData> mMediaUpdateData; @Mock Log.TerribleFailureHandler mFailHandler; @Mock Log.TerribleFailureHandler mFailHandler; @Mock MediaController mMockController; @Mock MediaController mMockController; @Mock MediaPlayerWrapper.Callback mTestCbs; @Mock MediaPlayerWrapper.Callback mTestCbs; Loading @@ -76,7 +76,7 @@ public class MediaPlayerWrapperTest { public void setUp() { public void setUp() { MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this); // Set failure handler to caputer Log.wtf messages // Set failure handler to capture Log.wtf messages Log.setWtfHandler(mFailHandler); Log.setWtfHandler(mFailHandler); // Set up Looper thread for the timeout handler // Set up Looper thread for the timeout handler Loading Loading @@ -127,7 +127,7 @@ public class MediaPlayerWrapperTest { doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); // Enable testing flag which enables Log.wtf statements. Some tests test against improper // Enable testing flag which enables Log.wtf statements. Some tests test against improper // behaviour and the TerribleFailureListener is a good way to ensure that the error occured // behaviour and the TerribleFailureListener is a good way to ensure that the error occurred MediaPlayerWrapper.sTesting = true; MediaPlayerWrapper.sTesting = true; } } Loading @@ -137,26 +137,55 @@ public class MediaPlayerWrapperTest { */ */ @Test @Test public void testNullControllerLooper() { public void testNullControllerLooper() { MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper(), false); MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(null, mThread.getLooper()); Assert.assertNull(wrapper); Assert.assertNull(wrapper); wrapper = MediaPlayerWrapper.wrap(mMockController, null, false); wrapper = MediaPlayerWrapper.wrap(mMockController, null); Assert.assertNull(wrapper); Assert.assertNull(wrapper); } } /* * Test to make sure that isReady() returns false if there is no playback state, * there is no metadata, or if the metadata has no title. */ @Test public void testIsReady() { MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); Assert.assertTrue(wrapper.isReady()); // Test isReady() is false when the playback state is null doReturn(null).when(mMockController).getPlaybackState(); Assert.assertFalse(wrapper.isReady()); // Restore the old playback state doReturn(mTestState.build()).when(mMockController).getPlaybackState(); Assert.assertTrue(wrapper.isReady()); // Test isReady() is false when the metadata is null doReturn(null).when(mMockController).getMetadata(); Assert.assertFalse(wrapper.isReady()); // Restore the old metadata doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); Assert.assertTrue(wrapper.isReady()); } /* /* * Test to make sure that a media player update gets sent whenever a Media metadata or playback * Test to make sure that a media player update gets sent whenever a Media metadata or playback * state change occurs instead of waiting for all data to be synced if the player doesn't * state change occurs instead of waiting for all data to be synced if the player doesn't * support browsing and queues. * support queues. */ */ @Test @Test public void testNoBrowsingMediaUpdates() { public void testNoQueueMediaUpdates() { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Return null when getting the queue doReturn(null).when(mMockController).getQueue(); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); Loading @@ -168,7 +197,7 @@ public class MediaPlayerWrapperTest { // Assert that the metadata was updated and playback state wasn't // Assert that the metadata was updated and playback state wasn't verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned Metadata isn't equal to given Metadata", "Returned Metadata isn't equal to given Metadata", data.metadata.getDescription(), data.metadata.getDescription(), Loading Loading @@ -202,58 +231,23 @@ public class MediaPlayerWrapperTest { verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); } } /* * Test that trying to get the queue on a player that doesn't support * browsing returns false. */ @Test public void testNoBrowsingNullQueue() { // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); // Update Queue returned by controller mTestQueue.add( new MediaDescription.Builder() .setTitle("New Title") .setSubtitle("BT Test Artist") .setDescription("BT Test Album") .setMediaId("103")); doReturn(getQueueFromDescriptions(mTestQueue)).when(mMockController).getQueue(); controllerCallbacks.onQueueChanged(getQueueFromDescriptions(mTestQueue)); // Verify no updates happened verify(mTestCbs, never()).mediaUpdatedCallback(any()); // Verify that getQueue() returns null Assert.assertNull(wrapper.getQueue()); // Verify that there was an error message pending and there were no timeouts Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT)); verify(mFailHandler, times(1)).onTerribleFailure(any(), any(), anyBoolean()); } /* /* * This test updates the metadata and playback state returned by the * This test updates the metadata and playback state returned by the * controller then sends an update. This is to make sure that all relevant * controller then sends an update. This is to make sure that all relevant * information is sent with every update. In the case without browsing, * information is sent with every update. In the case without a queue, * metadata and playback state are updated. * metadata and playback state are updated. */ */ @Test @Test public void testAllDataOnUpdate() { public void testDataOnUpdateNoQueue() { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Return null when getting the queue doReturn(null).when(mMockController).getQueue(); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); Loading @@ -271,7 +265,7 @@ public class MediaPlayerWrapperTest { // Assert that both metadata and playback state are there. // Assert that both metadata and playback state are there. verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned PlaybackState isn't equal to given PlaybackState", "Returned PlaybackState isn't equal to given PlaybackState", data.state.toString(), data.state.toString(), Loading @@ -288,7 +282,7 @@ public class MediaPlayerWrapperTest { } } /* /* * This test sends repeted Playback State updates that only have a short * This test sends repeated Playback State updates that only have a short * position update change to see if they get debounced. * position update change to see if they get debounced. */ */ @Test @Test Loading @@ -296,9 +290,12 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Return null when getting the queue doReturn(null).when(mMockController).getQueue(); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller verify(mMockController).registerCallback(mControllerCbs.capture(), any()); verify(mMockController).registerCallback(mControllerCbs.capture(), any()); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); MediaController.Callback controllerCallbacks = mControllerCbs.getValue(); Loading @@ -310,7 +307,7 @@ public class MediaPlayerWrapperTest { // Assert that both metadata and only the first playback state is there. // Assert that both metadata and only the first playback state is there. verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned PlaybackState isn't equal to given PlaybackState", "Returned PlaybackState isn't equal to given PlaybackState", data.state.toString(), data.state.toString(), Loading Loading @@ -350,7 +347,7 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Cleanup the wrapper // Cleanup the wrapper Loading @@ -370,7 +367,7 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), false); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -400,7 +397,7 @@ public class MediaPlayerWrapperTest { // Create the wrapper object and register the looper with the timeout handler // Create the wrapper object and register the looper with the timeout handler TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); TestLooperManager looperManager = new TestLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -430,7 +427,7 @@ public class MediaPlayerWrapperTest { // Assert that the callback was called with the updated data // Assert that the callback was called with the updated data verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned Metadata isn't equal to given Metadata", "Returned Metadata isn't equal to given Metadata", data.metadata.getDescription(), data.metadata.getDescription(), Loading Loading @@ -460,7 +457,7 @@ public class MediaPlayerWrapperTest { InstrumentationRegistry.getInstrumentation() InstrumentationRegistry.getInstrumentation() .acquireLooperManager(mThread.getLooper()); .acquireLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -494,7 +491,7 @@ public class MediaPlayerWrapperTest { InstrumentationRegistry.getInstrumentation() InstrumentationRegistry.getInstrumentation() .acquireLooperManager(mThread.getLooper()); .acquireLooperManager(mThread.getLooper()); MediaPlayerWrapper wrapper = MediaPlayerWrapper wrapper = MediaPlayerWrapper.wrap(mMockController, mThread.getLooper(), true); MediaPlayerWrapper.wrap(mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); wrapper.registerCallback(mTestCbs); // Grab the callbacks the wrapper registered with the controller // Grab the callbacks the wrapper registered with the controller Loading Loading @@ -545,7 +542,7 @@ public class MediaPlayerWrapperTest { // Check that the callback was called a certain number of times and // Check that the callback was called a certain number of times and // that all the Media info matches what was given // that all the Media info matches what was given verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture()); verify(mTestCbs, times(i)).mediaUpdatedCallback(mMediaUpdateData.capture()); MediaPlayerWrapper.MediaData data = mMediaUpdateData.getValue(); MediaData data = mMediaUpdateData.getValue(); Assert.assertEquals( Assert.assertEquals( "Returned Metadata isn't equal to given Metadata", "Returned Metadata isn't equal to given Metadata", data.metadata.getDescription(), data.metadata.getDescription(), Loading