Loading android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java +2 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,8 @@ public class Metadata implements Cloneable { + artist + "\" album=\"" + album + "\" genre=\"" + genre + "\" duration=" + duration + " trackPosition=" Loading android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java +34 −1 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ public class AvrcpTargetService extends ProfileService { boolean metadata = !Objects.equals(mCurrentData.metadata, data.metadata); boolean state = !MediaPlayerWrapper.playstateEquals(mCurrentData.state, data.state); boolean queue = !Objects.equals(mCurrentData.queue, data.queue); boolean queue = isQueueUpdated(mCurrentData.queue, data.queue); Log.d( TAG, Loading Loading @@ -562,6 +562,39 @@ public class AvrcpTargetService extends ProfileService { mNativeInterface.sendPlayerSettings(repeatMode, shuffleMode); } /** * Compares the {@link Metadata} of the current and new queues * * <p>Whenever the current playing track changed in the now playing list, its metadata is * updated. We should only send an update if the elements of the queue have been modified. * * <p>Only Title, Album and Artist metadata can be used for comparison. The metadata ID * corresponds to the position in the list and is not unique for each media. Genre, duration and * cover art are updated when the playing track changes as we are only able to retrieve this * information then. */ @VisibleForTesting public static boolean isQueueUpdated(List<Metadata> currentQueue, List<Metadata> newQueue) { if (newQueue == null && currentQueue == null) { return false; } if (newQueue == null || currentQueue == null || currentQueue.size() != newQueue.size()) { return true; } for (int index = 0; index < currentQueue.size(); index++) { Metadata currentMetadata = currentQueue.get(index); Metadata newMetadata = newQueue.get(index); if (!Objects.equals(currentMetadata.title, newMetadata.title) || !Objects.equals(currentMetadata.artist, newMetadata.artist) || !Objects.equals(currentMetadata.album, newMetadata.album)) { return true; } } return false; } /** Dump debugging information to the string builder */ public void dump(StringBuilder sb) { sb.append("\nProfile: AvrcpTargetService:\n"); Loading android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * Copyright 2024 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 static com.google.common.truth.Truth.assertThat; import android.net.Uri; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.audio_util.Image; import com.android.bluetooth.audio_util.Metadata; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class AvrcpTargetServiceTest { private static final String TEST_DATA = "-1"; @Test public void testQueueUpdateData() { List<Metadata> firstQueue = new ArrayList<Metadata>(); List<Metadata> secondQueue = new ArrayList<Metadata>(); firstQueue.add(createEmptyMetadata()); secondQueue.add(createEmptyMetadata()); assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isFalse(); secondQueue.add(createEmptyMetadata()); assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); firstQueue.add(createEmptyMetadata()); firstQueue.get(1).genre = TEST_DATA; firstQueue.get(1).mediaId = TEST_DATA; firstQueue.get(1).trackNum = TEST_DATA; firstQueue.get(1).numTracks = TEST_DATA; firstQueue.get(1).duration = TEST_DATA; firstQueue.get(1).image = new Image( InstrumentationRegistry.getInstrumentation().getTargetContext(), Uri.EMPTY); assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isFalse(); secondQueue.get(1).title = TEST_DATA; assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); secondQueue.set(1, createEmptyMetadata()); secondQueue.get(1).artist = TEST_DATA; assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); secondQueue.set(1, createEmptyMetadata()); secondQueue.get(1).album = TEST_DATA; assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); } private Metadata createEmptyMetadata() { Metadata.Builder builder = new Metadata.Builder(); return builder.useDefaults().build(); } } Loading
android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java +2 −0 Original line number Diff line number Diff line Loading @@ -101,6 +101,8 @@ public class Metadata implements Cloneable { + artist + "\" album=\"" + album + "\" genre=\"" + genre + "\" duration=" + duration + " trackPosition=" Loading
android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java +34 −1 Original line number Diff line number Diff line Loading @@ -116,7 +116,7 @@ public class AvrcpTargetService extends ProfileService { boolean metadata = !Objects.equals(mCurrentData.metadata, data.metadata); boolean state = !MediaPlayerWrapper.playstateEquals(mCurrentData.state, data.state); boolean queue = !Objects.equals(mCurrentData.queue, data.queue); boolean queue = isQueueUpdated(mCurrentData.queue, data.queue); Log.d( TAG, Loading Loading @@ -562,6 +562,39 @@ public class AvrcpTargetService extends ProfileService { mNativeInterface.sendPlayerSettings(repeatMode, shuffleMode); } /** * Compares the {@link Metadata} of the current and new queues * * <p>Whenever the current playing track changed in the now playing list, its metadata is * updated. We should only send an update if the elements of the queue have been modified. * * <p>Only Title, Album and Artist metadata can be used for comparison. The metadata ID * corresponds to the position in the list and is not unique for each media. Genre, duration and * cover art are updated when the playing track changes as we are only able to retrieve this * information then. */ @VisibleForTesting public static boolean isQueueUpdated(List<Metadata> currentQueue, List<Metadata> newQueue) { if (newQueue == null && currentQueue == null) { return false; } if (newQueue == null || currentQueue == null || currentQueue.size() != newQueue.size()) { return true; } for (int index = 0; index < currentQueue.size(); index++) { Metadata currentMetadata = currentQueue.get(index); Metadata newMetadata = newQueue.get(index); if (!Objects.equals(currentMetadata.title, newMetadata.title) || !Objects.equals(currentMetadata.artist, newMetadata.artist) || !Objects.equals(currentMetadata.album, newMetadata.album)) { return true; } } return false; } /** Dump debugging information to the string builder */ public void dump(StringBuilder sb) { sb.append("\nProfile: AvrcpTargetService:\n"); Loading
android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpTargetServiceTest.java 0 → 100644 +81 −0 Original line number Diff line number Diff line /* * Copyright 2024 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 static com.google.common.truth.Truth.assertThat; import android.net.Uri; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.audio_util.Image; import com.android.bluetooth.audio_util.Metadata; import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class AvrcpTargetServiceTest { private static final String TEST_DATA = "-1"; @Test public void testQueueUpdateData() { List<Metadata> firstQueue = new ArrayList<Metadata>(); List<Metadata> secondQueue = new ArrayList<Metadata>(); firstQueue.add(createEmptyMetadata()); secondQueue.add(createEmptyMetadata()); assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isFalse(); secondQueue.add(createEmptyMetadata()); assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); firstQueue.add(createEmptyMetadata()); firstQueue.get(1).genre = TEST_DATA; firstQueue.get(1).mediaId = TEST_DATA; firstQueue.get(1).trackNum = TEST_DATA; firstQueue.get(1).numTracks = TEST_DATA; firstQueue.get(1).duration = TEST_DATA; firstQueue.get(1).image = new Image( InstrumentationRegistry.getInstrumentation().getTargetContext(), Uri.EMPTY); assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isFalse(); secondQueue.get(1).title = TEST_DATA; assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); secondQueue.set(1, createEmptyMetadata()); secondQueue.get(1).artist = TEST_DATA; assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); secondQueue.set(1, createEmptyMetadata()); secondQueue.get(1).album = TEST_DATA; assertThat(AvrcpTargetService.isQueueUpdated(firstQueue, secondQueue)).isTrue(); } private Metadata createEmptyMetadata() { Metadata.Builder builder = new Metadata.Builder(); return builder.useDefaults().build(); } }