Loading android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java +17 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,23 @@ public class MediaPlayerWrapper { } List<Metadata> getCurrentQueue() { // MediaSession#QueueItem's MediaDescription doesn't necessarily include media duration, // so the playing media info metadata should be obtained by the MediaController. // MediaSession doesn't include the Playlist Metadata, only the current song one. Metadata mediaPlayingMetadata = getCurrentMetadata(); // The queue metadata is built with QueueId in place of MediaId, so we can't compare it. // MediaDescription is usually compared via its title, artist and album. if (mediaPlayingMetadata != null) { for (Metadata metadata : mCurrentData.queue) { if (metadata.title.equals(mediaPlayingMetadata.title) && metadata.artist.equals(mediaPlayingMetadata.artist) && metadata.album.equals(mediaPlayingMetadata.album)) { // Replace default values by MediaController non default values. metadata.replaceDefaults(mediaPlayingMetadata); } } } return mCurrentData.queue; } Loading android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java +47 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,14 @@ public class Metadata implements Cloneable { if (!Objects.equals(album, m.album)) return false; if (!Objects.equals(trackNum, m.trackNum)) return false; if (!Objects.equals(numTracks, m.numTracks)) return false; if (!Objects.equals(genre, m.genre)) return false; if (!Objects.equals(duration, m.duration)) return false; // Actual image comparisons have shown to be very expensive. Since it's rare that // an application changes the cover artwork between multiple images once it's not // null anymore, we just look for changes between "something" and "nothing". if ((image == null && m.image != null) || (image != null && m.image == null)) { return false; } return true; } Loading @@ -80,6 +88,45 @@ public class Metadata implements Cloneable { + " trackPosition=" + trackNum + "/" + numTracks + " image=" + image + " }"; } /** * Replaces default values by {@code filledMetadata} non default values. */ public void replaceDefaults(Metadata filledMetadata) { if (filledMetadata == null) { return; } Metadata empty = Util.empty_data(); if (empty.mediaId.equals(mediaId)) { mediaId = filledMetadata.mediaId; } if (empty.title.equals(title)) { title = filledMetadata.title; } if (empty.artist.equals(artist)) { artist = filledMetadata.artist; } if (empty.album.equals(album)) { album = filledMetadata.album; } if (empty.trackNum.equals(trackNum)) { trackNum = filledMetadata.trackNum; } if (empty.numTracks.equals(numTracks)) { numTracks = filledMetadata.numTracks; } if (empty.genre.equals(genre)) { genre = filledMetadata.genre; } if (empty.duration.equals(duration)) { duration = filledMetadata.duration; } if (image == null) { image = filledMetadata.image; } } /** * A Builder object to populate a Metadata from various different Media Framework objects */ Loading android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java +29 −2 Original line number Diff line number Diff line Loading @@ -412,17 +412,44 @@ public class MediaPlayerWrapperTest { MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); // Remove the current metadata so it does not override the default duration. doReturn(null).when(mMockController).getMetadata(); // Call getCurrentQueue() multiple times. for (int i = 0; i < 3; i++) { Assert.assertEquals(wrapper.getCurrentQueue(), Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue))); Assert.assertEquals( Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)), wrapper.getCurrentQueue()); } doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); // Verify that getQueue() was only called twice. Once on creation and once during // registration verify(mMockController, times(2)).getQueue(); } /* * This test checks if the currently playing song queue duration is completed * by the MediaController Metadata. */ @Test public void testQueueMetadata() { MediaPlayerWrapper wrapper = MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); doReturn(null).when(mMockController).getMetadata(); Assert.assertFalse(Util.toMetadata(mMockContext, mTestMetadata.build()).duration .equals(wrapper.getCurrentQueue().get(0).duration)); doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); Assert.assertEquals(Util.toMetadata(mMockContext, mTestMetadata.build()).duration, wrapper.getCurrentQueue().get(0).duration); // The MediaController Metadata should still not be equal to the queue // as the track count is different and should not be overridden. Assert.assertFalse(Util.toMetadata(mMockContext, mTestMetadata.build()) .equals(wrapper.getCurrentQueue().get(0))); } /* * This test sends repeated Playback State updates that only have a short * position update change to see if they get debounced. Loading Loading
android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java +17 −0 Original line number Diff line number Diff line Loading @@ -131,6 +131,23 @@ public class MediaPlayerWrapper { } List<Metadata> getCurrentQueue() { // MediaSession#QueueItem's MediaDescription doesn't necessarily include media duration, // so the playing media info metadata should be obtained by the MediaController. // MediaSession doesn't include the Playlist Metadata, only the current song one. Metadata mediaPlayingMetadata = getCurrentMetadata(); // The queue metadata is built with QueueId in place of MediaId, so we can't compare it. // MediaDescription is usually compared via its title, artist and album. if (mediaPlayingMetadata != null) { for (Metadata metadata : mCurrentData.queue) { if (metadata.title.equals(mediaPlayingMetadata.title) && metadata.artist.equals(mediaPlayingMetadata.artist) && metadata.album.equals(mediaPlayingMetadata.album)) { // Replace default values by MediaController non default values. metadata.replaceDefaults(mediaPlayingMetadata); } } } return mCurrentData.queue; } Loading
android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java +47 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,14 @@ public class Metadata implements Cloneable { if (!Objects.equals(album, m.album)) return false; if (!Objects.equals(trackNum, m.trackNum)) return false; if (!Objects.equals(numTracks, m.numTracks)) return false; if (!Objects.equals(genre, m.genre)) return false; if (!Objects.equals(duration, m.duration)) return false; // Actual image comparisons have shown to be very expensive. Since it's rare that // an application changes the cover artwork between multiple images once it's not // null anymore, we just look for changes between "something" and "nothing". if ((image == null && m.image != null) || (image != null && m.image == null)) { return false; } return true; } Loading @@ -80,6 +88,45 @@ public class Metadata implements Cloneable { + " trackPosition=" + trackNum + "/" + numTracks + " image=" + image + " }"; } /** * Replaces default values by {@code filledMetadata} non default values. */ public void replaceDefaults(Metadata filledMetadata) { if (filledMetadata == null) { return; } Metadata empty = Util.empty_data(); if (empty.mediaId.equals(mediaId)) { mediaId = filledMetadata.mediaId; } if (empty.title.equals(title)) { title = filledMetadata.title; } if (empty.artist.equals(artist)) { artist = filledMetadata.artist; } if (empty.album.equals(album)) { album = filledMetadata.album; } if (empty.trackNum.equals(trackNum)) { trackNum = filledMetadata.trackNum; } if (empty.numTracks.equals(numTracks)) { numTracks = filledMetadata.numTracks; } if (empty.genre.equals(genre)) { genre = filledMetadata.genre; } if (empty.duration.equals(duration)) { duration = filledMetadata.duration; } if (image == null) { image = filledMetadata.image; } } /** * A Builder object to populate a Metadata from various different Media Framework objects */ Loading
android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java +29 −2 Original line number Diff line number Diff line Loading @@ -412,17 +412,44 @@ public class MediaPlayerWrapperTest { MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); wrapper.registerCallback(mTestCbs); // Remove the current metadata so it does not override the default duration. doReturn(null).when(mMockController).getMetadata(); // Call getCurrentQueue() multiple times. for (int i = 0; i < 3; i++) { Assert.assertEquals(wrapper.getCurrentQueue(), Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue))); Assert.assertEquals( Util.toMetadataList(mMockContext, getQueueFromDescriptions(mTestQueue)), wrapper.getCurrentQueue()); } doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); // Verify that getQueue() was only called twice. Once on creation and once during // registration verify(mMockController, times(2)).getQueue(); } /* * This test checks if the currently playing song queue duration is completed * by the MediaController Metadata. */ @Test public void testQueueMetadata() { MediaPlayerWrapper wrapper = MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper()); doReturn(null).when(mMockController).getMetadata(); Assert.assertFalse(Util.toMetadata(mMockContext, mTestMetadata.build()).duration .equals(wrapper.getCurrentQueue().get(0).duration)); doReturn(mTestMetadata.build()).when(mMockController).getMetadata(); Assert.assertEquals(Util.toMetadata(mMockContext, mTestMetadata.build()).duration, wrapper.getCurrentQueue().get(0).duration); // The MediaController Metadata should still not be equal to the queue // as the track count is different and should not be overridden. Assert.assertFalse(Util.toMetadata(mMockContext, mTestMetadata.build()) .equals(wrapper.getCurrentQueue().get(0))); } /* * This test sends repeated Playback State updates that only have a short * position update change to see if they get debounced. Loading