Add an artworkDataType to MediaMetadata.

This field is to be associated with the artworkData.

PiperOrigin-RevId: 385757480
This commit is contained in:
samrobinson 2021-07-20 12:29:01 +01:00 committed by Ian Baker
parent 0fe504d4b1
commit fde3075af1
4 changed files with 176 additions and 9 deletions

View file

@ -49,6 +49,7 @@ public final class MediaMetadata implements Bundleable {
@Nullable private Rating userRating;
@Nullable private Rating overallRating;
@Nullable private byte[] artworkData;
@Nullable @PictureType private Integer artworkDataType;
@Nullable private Uri artworkUri;
@Nullable private Integer trackNumber;
@Nullable private Integer totalTrackCount;
@ -83,6 +84,7 @@ public final class MediaMetadata implements Bundleable {
this.userRating = mediaMetadata.userRating;
this.overallRating = mediaMetadata.overallRating;
this.artworkData = mediaMetadata.artworkData;
this.artworkDataType = mediaMetadata.artworkDataType;
this.artworkUri = mediaMetadata.artworkUri;
this.trackNumber = mediaMetadata.trackNumber;
this.totalTrackCount = mediaMetadata.totalTrackCount;
@ -168,9 +170,41 @@ public final class MediaMetadata implements Bundleable {
return this;
}
/** Sets the artwork data as a compressed byte array. */
/**
* @deprecated Use {@link #setArtworkData(byte[] data, Integer pictureType)} or {@link
* #maybeSetArtworkData(byte[] data, int pictureType)}, providing a {@link PictureType}.
*/
@Deprecated
public Builder setArtworkData(@Nullable byte[] artworkData) {
return setArtworkData(artworkData, /* artworkDataType= */ null);
}
/**
* Sets the artwork data as a compressed byte array with an associated {@link PictureType
* artworkDataType}.
*/
public Builder setArtworkData(
@Nullable byte[] artworkData, @Nullable @PictureType Integer artworkDataType) {
this.artworkData = artworkData == null ? null : artworkData.clone();
this.artworkDataType = artworkDataType;
return this;
}
/**
* Sets the artwork data as a compressed byte array in the event that the associated {@link
* PictureType} is {@link #PICTURE_TYPE_FRONT_COVER}, the existing {@link PictureType} is not
* {@link #PICTURE_TYPE_FRONT_COVER}, or the current artworkData is not set.
*
* <p>Use {@link #setArtworkData(byte[], Integer)} to set the artwork data without checking the
* {@link PictureType}.
*/
public Builder maybeSetArtworkData(byte[] artworkData, @PictureType int artworkDataType) {
if (this.artworkData == null
|| Util.areEqual(artworkDataType, PICTURE_TYPE_FRONT_COVER)
|| !Util.areEqual(this.artworkDataType, PICTURE_TYPE_FRONT_COVER)) {
this.artworkData = artworkData.clone();
this.artworkDataType = artworkDataType;
}
return this;
}
@ -393,6 +427,61 @@ public final class MediaMetadata implements Bundleable {
/** Type for a folder containing media categorized by year. */
public static final int FOLDER_TYPE_YEARS = 6;
/**
* The picture type of the artwork.
*
* <p>Values sourced from the ID3 v2.4 specification (See section 4.14 of
* https://id3.org/id3v2.4.0-frames).
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PICTURE_TYPE_OTHER,
PICTURE_TYPE_FILE_ICON,
PICTURE_TYPE_FILE_ICON_OTHER,
PICTURE_TYPE_FRONT_COVER,
PICTURE_TYPE_BACK_COVER,
PICTURE_TYPE_LEAFLET_PAGE,
PICTURE_TYPE_MEDIA,
PICTURE_TYPE_LEAD_ARTIST_PERFORMER,
PICTURE_TYPE_ARTIST_PERFORMER,
PICTURE_TYPE_CONDUCTOR,
PICTURE_TYPE_BAND_ORCHESTRA,
PICTURE_TYPE_COMPOSER,
PICTURE_TYPE_LYRICIST,
PICTURE_TYPE_RECORDING_LOCATION,
PICTURE_TYPE_DURING_RECORDING,
PICTURE_TYPE_DURING_PERFORMANCE,
PICTURE_TYPE_MOVIE_VIDEO_SCREEN_CAPTURE,
PICTURE_TYPE_A_BRIGHT_COLORED_FISH,
PICTURE_TYPE_ILLUSTRATION,
PICTURE_TYPE_BAND_ARTIST_LOGO,
PICTURE_TYPE_PUBLISHER_STUDIO_LOGO
})
public @interface PictureType {}
public static final int PICTURE_TYPE_OTHER = 0x00;
public static final int PICTURE_TYPE_FILE_ICON = 0x01;
public static final int PICTURE_TYPE_FILE_ICON_OTHER = 0x02;
public static final int PICTURE_TYPE_FRONT_COVER = 0x03;
public static final int PICTURE_TYPE_BACK_COVER = 0x04;
public static final int PICTURE_TYPE_LEAFLET_PAGE = 0x05;
public static final int PICTURE_TYPE_MEDIA = 0x06;
public static final int PICTURE_TYPE_LEAD_ARTIST_PERFORMER = 0x07;
public static final int PICTURE_TYPE_ARTIST_PERFORMER = 0x08;
public static final int PICTURE_TYPE_CONDUCTOR = 0x09;
public static final int PICTURE_TYPE_BAND_ORCHESTRA = 0x0A;
public static final int PICTURE_TYPE_COMPOSER = 0x0B;
public static final int PICTURE_TYPE_LYRICIST = 0x0C;
public static final int PICTURE_TYPE_RECORDING_LOCATION = 0x0D;
public static final int PICTURE_TYPE_DURING_RECORDING = 0x0E;
public static final int PICTURE_TYPE_DURING_PERFORMANCE = 0x0F;
public static final int PICTURE_TYPE_MOVIE_VIDEO_SCREEN_CAPTURE = 0x10;
public static final int PICTURE_TYPE_A_BRIGHT_COLORED_FISH = 0x11;
public static final int PICTURE_TYPE_ILLUSTRATION = 0x12;
public static final int PICTURE_TYPE_BAND_ARTIST_LOGO = 0x13;
public static final int PICTURE_TYPE_PUBLISHER_STUDIO_LOGO = 0x14;
/** Empty {@link MediaMetadata}. */
public static final MediaMetadata EMPTY = new MediaMetadata.Builder().build();
@ -422,6 +511,8 @@ public final class MediaMetadata implements Bundleable {
@Nullable public final Rating overallRating;
/** Optional artwork data as a compressed byte array. */
@Nullable public final byte[] artworkData;
/** Optional {@link PictureType} of the artwork data. */
@Nullable @PictureType public final Integer artworkDataType;
/** Optional artwork {@link Uri}. */
@Nullable public final Uri artworkUri;
/** Optional track number. */
@ -498,6 +589,7 @@ public final class MediaMetadata implements Bundleable {
this.userRating = builder.userRating;
this.overallRating = builder.overallRating;
this.artworkData = builder.artworkData;
this.artworkDataType = builder.artworkDataType;
this.artworkUri = builder.artworkUri;
this.trackNumber = builder.trackNumber;
this.totalTrackCount = builder.totalTrackCount;
@ -545,6 +637,7 @@ public final class MediaMetadata implements Bundleable {
&& Util.areEqual(userRating, that.userRating)
&& Util.areEqual(overallRating, that.overallRating)
&& Arrays.equals(artworkData, that.artworkData)
&& Util.areEqual(artworkDataType, that.artworkDataType)
&& Util.areEqual(artworkUri, that.artworkUri)
&& Util.areEqual(trackNumber, that.trackNumber)
&& Util.areEqual(totalTrackCount, that.totalTrackCount)
@ -579,6 +672,7 @@ public final class MediaMetadata implements Bundleable {
userRating,
overallRating,
Arrays.hashCode(artworkData),
artworkDataType,
artworkUri,
trackNumber,
totalTrackCount,
@ -615,6 +709,7 @@ public final class MediaMetadata implements Bundleable {
FIELD_USER_RATING,
FIELD_OVERALL_RATING,
FIELD_ARTWORK_DATA,
FIELD_ARTWORK_DATA_TYPE,
FIELD_ARTWORK_URI,
FIELD_TRACK_NUMBER,
FIELD_TOTAL_TRACK_COUNT,
@ -666,6 +761,7 @@ public final class MediaMetadata implements Bundleable {
private static final int FIELD_TOTAL_DISC_COUNT = 26;
private static final int FIELD_GENRE = 27;
private static final int FIELD_COMPILATION = 28;
private static final int FIELD_ARTWORK_DATA_TYPE = 29;
private static final int FIELD_EXTRAS = 1000;
@Override
@ -729,6 +825,9 @@ public final class MediaMetadata implements Bundleable {
if (totalDiscCount != null) {
bundle.putInt(keyForField(FIELD_TOTAL_DISC_COUNT), totalDiscCount);
}
if (artworkDataType != null) {
bundle.putInt(keyForField(FIELD_ARTWORK_DATA_TYPE), artworkDataType);
}
if (extras != null) {
bundle.putBundle(keyForField(FIELD_EXTRAS), extras);
}
@ -749,7 +848,11 @@ public final class MediaMetadata implements Bundleable {
.setSubtitle(bundle.getCharSequence(keyForField(FIELD_SUBTITLE)))
.setDescription(bundle.getCharSequence(keyForField(FIELD_DESCRIPTION)))
.setMediaUri(bundle.getParcelable(keyForField(FIELD_MEDIA_URI)))
.setArtworkData(bundle.getByteArray(keyForField(FIELD_ARTWORK_DATA)))
.setArtworkData(
bundle.getByteArray(keyForField(FIELD_ARTWORK_DATA)),
bundle.containsKey(keyForField(FIELD_ARTWORK_DATA_TYPE))
? bundle.getInt(keyForField(FIELD_ARTWORK_DATA_TYPE))
: null)
.setArtworkUri(bundle.getParcelable(keyForField(FIELD_ARTWORK_URI)))
.setWriter(bundle.getCharSequence(keyForField(FIELD_WRITER)))
.setComposer(bundle.getCharSequence(keyForField(FIELD_COMPOSER)))

View file

@ -76,7 +76,7 @@ public final class PictureFrame implements Metadata.Entry {
@Override
public void populateMediaMetadata(MediaMetadata.Builder builder) {
builder.setArtworkData(pictureData);
builder.maybeSetArtworkData(pictureData, pictureType);
}
@Override

View file

@ -53,7 +53,7 @@ public final class ApicFrame extends Id3Frame {
@Override
public void populateMediaMetadata(MediaMetadata.Builder builder) {
builder.setArtworkData(pictureData);
builder.maybeSetArtworkData(pictureData, pictureType);
}
@Override

View file

@ -20,10 +20,10 @@ import static com.google.common.truth.Truth.assertThat;
import android.net.Uri;
import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.MediaMetadata.PictureType;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.ApicFrame;
import com.google.android.exoplayer2.util.MimeTypes;
import java.util.Arrays;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -46,6 +46,7 @@ public class MediaMetadataTest {
assertThat(mediaMetadata.userRating).isNull();
assertThat(mediaMetadata.overallRating).isNull();
assertThat(mediaMetadata.artworkData).isNull();
assertThat(mediaMetadata.artworkDataType).isNull();
assertThat(mediaMetadata.artworkUri).isNull();
assertThat(mediaMetadata.trackNumber).isNull();
assertThat(mediaMetadata.totalTrackCount).isNull();
@ -79,9 +80,10 @@ public class MediaMetadataTest {
@Test
public void builderSetArtworkData_setsArtworkData() {
byte[] bytes = new byte[] {35, 12, 6, 77};
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setArtworkData(bytes).build();
MediaMetadata mediaMetadata =
new MediaMetadata.Builder().setArtworkData(new byte[] {35, 12, 6, 77}, null).build();
assertThat(Arrays.equals(mediaMetadata.artworkData, bytes)).isTrue();
assertThat(mediaMetadata.artworkData).isEqualTo(bytes);
}
@Test
@ -104,7 +106,8 @@ public class MediaMetadataTest {
.setMediaUri(Uri.parse("https://www.google.com"))
.setUserRating(new HeartRating(false))
.setOverallRating(new PercentageRating(87.4f))
.setArtworkData(new byte[] {-88, 12, 3, 2, 124, -54, -33, 69})
.setArtworkData(
new byte[] {-88, 12, 3, 2, 124, -54, -33, 69}, MediaMetadata.PICTURE_TYPE_MEDIA)
.setTrackNumber(4)
.setTotalTrackCount(12)
.setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS)
@ -133,11 +136,12 @@ public class MediaMetadataTest {
@Test
public void builderPopulatedFromApicFrameEntry_setsArtwork() {
byte[] pictureData = new byte[] {-12, 52, 33, 85, 34, 22, 1, -55};
@PictureType int pictureType = MediaMetadata.PICTURE_TYPE_LEAFLET_PAGE;
Metadata.Entry entry =
new ApicFrame(
/* mimeType= */ MimeTypes.BASE_TYPE_IMAGE,
/* description= */ "an image",
/* pictureType= */ 0x03,
pictureType,
pictureData);
MediaMetadata.Builder builder = MediaMetadata.EMPTY.buildUpon();
@ -145,5 +149,65 @@ public class MediaMetadataTest {
MediaMetadata mediaMetadata = builder.build();
assertThat(mediaMetadata.artworkData).isEqualTo(pictureData);
assertThat(mediaMetadata.artworkDataType).isEqualTo(pictureType);
}
@Test
public void builderPopulatedFromApicFrameEntry_considersTypePriority() {
byte[] data1 = new byte[] {1, 1, 1, 1};
Metadata.Entry entry1 =
new ApicFrame(
/* mimeType= */ MimeTypes.BASE_TYPE_IMAGE,
/* description= */ "an image",
MediaMetadata.PICTURE_TYPE_BAND_ARTIST_LOGO,
data1);
byte[] data2 = new byte[] {2, 2, 2, 2};
Metadata.Entry entry2 =
new ApicFrame(
/* mimeType= */ MimeTypes.BASE_TYPE_IMAGE,
/* description= */ "an image",
MediaMetadata.PICTURE_TYPE_ARTIST_PERFORMER,
data2);
byte[] data3 = new byte[] {3, 3, 3, 3};
Metadata.Entry entry3 =
new ApicFrame(
/* mimeType= */ MimeTypes.BASE_TYPE_IMAGE,
/* description= */ "an image",
MediaMetadata.PICTURE_TYPE_FRONT_COVER,
data3);
byte[] data4 = new byte[] {4, 4, 4, 4};
Metadata.Entry entry4 =
new ApicFrame(
/* mimeType= */ MimeTypes.BASE_TYPE_IMAGE,
/* description= */ "an image",
MediaMetadata.PICTURE_TYPE_ILLUSTRATION,
data4);
byte[] data5 = new byte[] {5, 5, 5, 5};
Metadata.Entry entry5 =
new ApicFrame(
/* mimeType= */ MimeTypes.BASE_TYPE_IMAGE,
/* description= */ "an image",
MediaMetadata.PICTURE_TYPE_FRONT_COVER,
data5);
MediaMetadata.Builder builder = MediaMetadata.EMPTY.buildUpon();
entry1.populateMediaMetadata(builder);
assertThat(builder.build().artworkData).isEqualTo(data1);
// Data updates when any type is given, if the current type is not front cover.
entry2.populateMediaMetadata(builder);
assertThat(builder.build().artworkData).isEqualTo(data2);
// Data updates because this entry picture type is front cover.
entry3.populateMediaMetadata(builder);
assertThat(builder.build().artworkData).isEqualTo(data3);
// Data does not update because the current type is front cover, and this entry type is not.
entry4.populateMediaMetadata(builder);
assertThat(builder.build().artworkData).isEqualTo(data3);
// Data updates because this entry picture type is front cover.
entry5.populateMediaMetadata(builder);
assertThat(builder.build().artworkData).isEqualTo(data5);
}
}