Commit d1aacc5f by samrobinson Committed by Oliver Woodman

Add release date and recording date fields to MediaMetadata.

PiperOrigin-RevId: 379962022
parent 78076035
...@@ -15,9 +15,12 @@ ...@@ -15,9 +15,12 @@
*/ */
package com.google.android.exoplayer2; package com.google.android.exoplayer2;
import static com.google.android.exoplayer2.util.Assertions.checkArgument;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -53,7 +56,12 @@ public final class MediaMetadata implements Bundleable { ...@@ -53,7 +56,12 @@ public final class MediaMetadata implements Bundleable {
@Nullable private Integer totalTrackCount; @Nullable private Integer totalTrackCount;
@Nullable @FolderType private Integer folderType; @Nullable @FolderType private Integer folderType;
@Nullable private Boolean isPlayable; @Nullable private Boolean isPlayable;
@Nullable private Integer year; @Nullable private Integer recordingYear;
@Nullable private Integer recordingMonth;
@Nullable private Integer recordingDay;
@Nullable private Integer releaseYear;
@Nullable private Integer releaseMonth;
@Nullable private Integer releaseDay;
@Nullable private CharSequence writer; @Nullable private CharSequence writer;
@Nullable private CharSequence composer; @Nullable private CharSequence composer;
@Nullable private CharSequence conductor; @Nullable private CharSequence conductor;
...@@ -80,7 +88,12 @@ public final class MediaMetadata implements Bundleable { ...@@ -80,7 +88,12 @@ public final class MediaMetadata implements Bundleable {
this.totalTrackCount = mediaMetadata.totalTrackCount; this.totalTrackCount = mediaMetadata.totalTrackCount;
this.folderType = mediaMetadata.folderType; this.folderType = mediaMetadata.folderType;
this.isPlayable = mediaMetadata.isPlayable; this.isPlayable = mediaMetadata.isPlayable;
this.year = mediaMetadata.year; this.recordingYear = mediaMetadata.recordingYear;
this.recordingMonth = mediaMetadata.recordingMonth;
this.recordingDay = mediaMetadata.recordingDay;
this.releaseYear = mediaMetadata.releaseYear;
this.releaseMonth = mediaMetadata.releaseMonth;
this.releaseDay = mediaMetadata.releaseDay;
this.writer = mediaMetadata.writer; this.writer = mediaMetadata.writer;
this.composer = mediaMetadata.composer; this.composer = mediaMetadata.composer;
this.conductor = mediaMetadata.conductor; this.conductor = mediaMetadata.conductor;
...@@ -189,9 +202,74 @@ public final class MediaMetadata implements Bundleable { ...@@ -189,9 +202,74 @@ public final class MediaMetadata implements Bundleable {
return this; return this;
} }
/** Sets the year. */ /** @deprecated Use {@link #setRecordingYear(Integer)} instead. */
@Deprecated
public Builder setYear(@Nullable Integer year) { public Builder setYear(@Nullable Integer year) {
this.year = year; return setRecordingYear(year);
}
/** Sets the year of the recording date. */
public Builder setRecordingYear(@Nullable Integer recordingYear) {
this.recordingYear = recordingYear;
return this;
}
/**
* Sets the month of the recording date.
*
* <p>Value must be between 1 and 12.
*/
public Builder setRecordingMonth(
@Nullable @IntRange(from = 1, to = 12) Integer recordingMonth) {
if (recordingMonth != null) {
checkArgument(recordingMonth >= 1 && recordingMonth <= 12);
}
this.recordingMonth = recordingMonth;
return this;
}
/**
* Sets the day of the recording date.
*
* <p>Value must be between 1 and 31.
*/
public Builder setRecordingDay(@Nullable @IntRange(from = 1, to = 31) Integer recordingDay) {
if (recordingDay != null) {
checkArgument(recordingDay >= 1 && recordingDay <= 31);
}
this.recordingDay = recordingDay;
return this;
}
/** Sets the year of the release date. */
public Builder setReleaseYear(@Nullable Integer releaseYear) {
this.releaseYear = releaseYear;
return this;
}
/**
* Sets the month of the release date.
*
* <p>Value must be between 1 and 12.
*/
public Builder setReleaseMonth(@Nullable @IntRange(from = 1, to = 12) Integer releaseMonth) {
if (releaseMonth != null) {
checkArgument(releaseMonth >= 1 && releaseMonth <= 12);
}
this.releaseMonth = releaseMonth;
return this;
}
/**
* Sets the day of the release date.
*
* <p>Value must be between 1 and 31.
*/
public Builder setReleaseDay(@Nullable @IntRange(from = 1, to = 31) Integer releaseDay) {
if (releaseDay != null) {
checkArgument(releaseDay >= 1 && releaseDay <= 31);
}
this.releaseDay = releaseDay;
return this; return this;
} }
...@@ -352,8 +430,37 @@ public final class MediaMetadata implements Bundleable { ...@@ -352,8 +430,37 @@ public final class MediaMetadata implements Bundleable {
@Nullable @FolderType public final Integer folderType; @Nullable @FolderType public final Integer folderType;
/** Optional boolean for media playability. */ /** Optional boolean for media playability. */
@Nullable public final Boolean isPlayable; @Nullable public final Boolean isPlayable;
/** Optional year. */ /** @deprecated Use {@link #recordingYear} instead. */
@Nullable public final Integer year; @Deprecated @Nullable public final Integer year;
/** Optional year of the recording date. */
@Nullable public final Integer recordingYear;
/**
* Optional month of the recording date.
*
* <p>Note that there is no guarantee that the month and day are a valid combination.
*/
@Nullable public final Integer recordingMonth;
/**
* Optional day of the recording date.
*
* <p>Note that there is no guarantee that the month and day are a valid combination.
*/
@Nullable public final Integer recordingDay;
/** Optional year of the release date. */
@Nullable public final Integer releaseYear;
/**
* Optional month of the release date.
*
* <p>Note that there is no guarantee that the month and day are a valid combination.
*/
@Nullable public final Integer releaseMonth;
/**
* Optional day of the release date.
*
* <p>Note that there is no guarantee that the month and day are a valid combination.
*/
@Nullable public final Integer releaseDay;
/** Optional writer. */ /** Optional writer. */
@Nullable public final CharSequence writer; @Nullable public final CharSequence writer;
/** Optional composer. */ /** Optional composer. */
...@@ -390,7 +497,13 @@ public final class MediaMetadata implements Bundleable { ...@@ -390,7 +497,13 @@ public final class MediaMetadata implements Bundleable {
this.totalTrackCount = builder.totalTrackCount; this.totalTrackCount = builder.totalTrackCount;
this.folderType = builder.folderType; this.folderType = builder.folderType;
this.isPlayable = builder.isPlayable; this.isPlayable = builder.isPlayable;
this.year = builder.year; this.year = builder.recordingYear;
this.recordingYear = builder.recordingYear;
this.recordingMonth = builder.recordingMonth;
this.recordingDay = builder.recordingDay;
this.releaseYear = builder.releaseYear;
this.releaseMonth = builder.releaseMonth;
this.releaseDay = builder.releaseDay;
this.writer = builder.writer; this.writer = builder.writer;
this.composer = builder.composer; this.composer = builder.composer;
this.conductor = builder.conductor; this.conductor = builder.conductor;
...@@ -429,7 +542,12 @@ public final class MediaMetadata implements Bundleable { ...@@ -429,7 +542,12 @@ public final class MediaMetadata implements Bundleable {
&& Util.areEqual(totalTrackCount, that.totalTrackCount) && Util.areEqual(totalTrackCount, that.totalTrackCount)
&& Util.areEqual(folderType, that.folderType) && Util.areEqual(folderType, that.folderType)
&& Util.areEqual(isPlayable, that.isPlayable) && Util.areEqual(isPlayable, that.isPlayable)
&& Util.areEqual(year, that.year) && Util.areEqual(recordingYear, that.recordingYear)
&& Util.areEqual(recordingMonth, that.recordingMonth)
&& Util.areEqual(recordingDay, that.recordingDay)
&& Util.areEqual(releaseYear, that.releaseYear)
&& Util.areEqual(releaseMonth, that.releaseMonth)
&& Util.areEqual(releaseDay, that.releaseDay)
&& Util.areEqual(writer, that.writer) && Util.areEqual(writer, that.writer)
&& Util.areEqual(composer, that.composer) && Util.areEqual(composer, that.composer)
&& Util.areEqual(conductor, that.conductor) && Util.areEqual(conductor, that.conductor)
...@@ -456,7 +574,12 @@ public final class MediaMetadata implements Bundleable { ...@@ -456,7 +574,12 @@ public final class MediaMetadata implements Bundleable {
totalTrackCount, totalTrackCount,
folderType, folderType,
isPlayable, isPlayable,
year, recordingYear,
recordingMonth,
recordingDay,
releaseYear,
releaseMonth,
releaseDay,
writer, writer,
composer, composer,
conductor, conductor,
...@@ -485,7 +608,12 @@ public final class MediaMetadata implements Bundleable { ...@@ -485,7 +608,12 @@ public final class MediaMetadata implements Bundleable {
FIELD_TOTAL_TRACK_COUNT, FIELD_TOTAL_TRACK_COUNT,
FIELD_FOLDER_TYPE, FIELD_FOLDER_TYPE,
FIELD_IS_PLAYABLE, FIELD_IS_PLAYABLE,
FIELD_YEAR, FIELD_RECORDING_YEAR,
FIELD_RECORDING_MONTH,
FIELD_RECORDING_DAY,
FIELD_RELEASE_YEAR,
FIELD_RELEASE_MONTH,
FIELD_RELEASE_DAY,
FIELD_WRITER, FIELD_WRITER,
FIELD_COMPOSER, FIELD_COMPOSER,
FIELD_CONDUCTOR, FIELD_CONDUCTOR,
...@@ -511,12 +639,17 @@ public final class MediaMetadata implements Bundleable { ...@@ -511,12 +639,17 @@ public final class MediaMetadata implements Bundleable {
private static final int FIELD_TOTAL_TRACK_COUNT = 13; private static final int FIELD_TOTAL_TRACK_COUNT = 13;
private static final int FIELD_FOLDER_TYPE = 14; private static final int FIELD_FOLDER_TYPE = 14;
private static final int FIELD_IS_PLAYABLE = 15; private static final int FIELD_IS_PLAYABLE = 15;
private static final int FIELD_YEAR = 16; private static final int FIELD_RECORDING_YEAR = 16;
private static final int FIELD_WRITER = 17; private static final int FIELD_RECORDING_MONTH = 17;
private static final int FIELD_COMPOSER = 18; private static final int FIELD_RECORDING_DAY = 18;
private static final int FIELD_CONDUCTOR = 19; private static final int FIELD_RELEASE_YEAR = 19;
private static final int FIELD_DISC_NUMBER = 20; private static final int FIELD_RELEASE_MONTH = 20;
private static final int FIELD_TOTAL_DISC_COUNT = 21; private static final int FIELD_RELEASE_DAY = 21;
private static final int FIELD_WRITER = 22;
private static final int FIELD_COMPOSER = 23;
private static final int FIELD_CONDUCTOR = 24;
private static final int FIELD_DISC_NUMBER = 25;
private static final int FIELD_TOTAL_DISC_COUNT = 26;
private static final int FIELD_EXTRAS = 1000; private static final int FIELD_EXTRAS = 1000;
@Override @Override
...@@ -554,8 +687,23 @@ public final class MediaMetadata implements Bundleable { ...@@ -554,8 +687,23 @@ public final class MediaMetadata implements Bundleable {
if (isPlayable != null) { if (isPlayable != null) {
bundle.putBoolean(keyForField(FIELD_IS_PLAYABLE), isPlayable); bundle.putBoolean(keyForField(FIELD_IS_PLAYABLE), isPlayable);
} }
if (year != null) { if (recordingYear != null) {
bundle.putInt(keyForField(FIELD_YEAR), year); bundle.putInt(keyForField(FIELD_RECORDING_YEAR), recordingYear);
}
if (recordingMonth != null) {
bundle.putInt(keyForField(FIELD_RECORDING_MONTH), recordingMonth);
}
if (recordingDay != null) {
bundle.putInt(keyForField(FIELD_RECORDING_DAY), recordingDay);
}
if (releaseYear != null) {
bundle.putInt(keyForField(FIELD_RELEASE_YEAR), releaseYear);
}
if (releaseMonth != null) {
bundle.putInt(keyForField(FIELD_RELEASE_MONTH), releaseMonth);
}
if (releaseDay != null) {
bundle.putInt(keyForField(FIELD_RELEASE_DAY), releaseDay);
} }
if (discNumber != null) { if (discNumber != null) {
bundle.putInt(keyForField(FIELD_DISC_NUMBER), discNumber); bundle.putInt(keyForField(FIELD_DISC_NUMBER), discNumber);
...@@ -614,8 +762,23 @@ public final class MediaMetadata implements Bundleable { ...@@ -614,8 +762,23 @@ public final class MediaMetadata implements Bundleable {
if (bundle.containsKey(keyForField(FIELD_IS_PLAYABLE))) { if (bundle.containsKey(keyForField(FIELD_IS_PLAYABLE))) {
builder.setIsPlayable(bundle.getBoolean(keyForField(FIELD_IS_PLAYABLE))); builder.setIsPlayable(bundle.getBoolean(keyForField(FIELD_IS_PLAYABLE)));
} }
if (bundle.containsKey(keyForField(FIELD_YEAR))) { if (bundle.containsKey(keyForField(FIELD_RECORDING_YEAR))) {
builder.setYear(bundle.getInt(keyForField(FIELD_YEAR))); builder.setRecordingYear(bundle.getInt(keyForField(FIELD_RECORDING_YEAR)));
}
if (bundle.containsKey(keyForField(FIELD_RECORDING_MONTH))) {
builder.setRecordingMonth(bundle.getInt(keyForField(FIELD_RECORDING_MONTH)));
}
if (bundle.containsKey(keyForField(FIELD_RECORDING_DAY))) {
builder.setRecordingDay(bundle.getInt(keyForField(FIELD_RECORDING_DAY)));
}
if (bundle.containsKey(keyForField(FIELD_RELEASE_YEAR))) {
builder.setReleaseYear(bundle.getInt(keyForField(FIELD_RELEASE_YEAR)));
}
if (bundle.containsKey(keyForField(FIELD_RELEASE_MONTH))) {
builder.setReleaseMonth(bundle.getInt(keyForField(FIELD_RELEASE_MONTH)));
}
if (bundle.containsKey(keyForField(FIELD_RELEASE_DAY))) {
builder.setReleaseDay(bundle.getInt(keyForField(FIELD_RELEASE_DAY)));
} }
if (bundle.containsKey(keyForField(FIELD_DISC_NUMBER))) { if (bundle.containsKey(keyForField(FIELD_DISC_NUMBER))) {
builder.setDiscNumber(bundle.getInt(keyForField(FIELD_DISC_NUMBER))); builder.setDiscNumber(bundle.getInt(keyForField(FIELD_DISC_NUMBER)));
......
...@@ -22,6 +22,8 @@ import android.os.Parcelable; ...@@ -22,6 +22,8 @@ import android.os.Parcelable;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.MediaMetadata;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.List;
/** Text information ID3 frame. */ /** Text information ID3 frame. */
public final class TextInformationFrame extends Id3Frame { public final class TextInformationFrame extends Id3Frame {
...@@ -76,11 +78,57 @@ public final class TextInformationFrame extends Id3Frame { ...@@ -76,11 +78,57 @@ public final class TextInformationFrame extends Id3Frame {
case "TYE": case "TYE":
case "TYER": case "TYER":
try { try {
builder.setYear(Integer.parseInt(value)); builder.setRecordingYear(Integer.parseInt(value));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// Do nothing, invalid input. // Do nothing, invalid input.
} }
break; break;
case "TDA":
case "TDAT":
try {
int month = Integer.parseInt(value.substring(2, 4));
int day = Integer.parseInt(value.substring(0, 2));
builder.setRecordingMonth(month).setRecordingDay(day);
} catch (NumberFormatException | StringIndexOutOfBoundsException e) {
// Do nothing, invalid input.
}
break;
case "TDRC":
List<Integer> recordingDate = parseId3v2point4TimestampFrameForDate(value);
switch (recordingDate.size()) {
case 3:
builder.setRecordingDay(recordingDate.get(2));
// fall through
case 2:
builder.setRecordingMonth(recordingDate.get(1));
// fall through
case 1:
builder.setRecordingYear(recordingDate.get(0));
// fall through
break;
default:
// Do nothing.
break;
}
break;
case "TDRL":
List<Integer> releaseDate = parseId3v2point4TimestampFrameForDate(value);
switch (releaseDate.size()) {
case 3:
builder.setReleaseDay(releaseDate.get(2));
// fall through
case 2:
builder.setReleaseMonth(releaseDate.get(1));
// fall through
case 1:
builder.setReleaseYear(releaseDate.get(0));
// fall through
break;
default:
// Do nothing.
break;
}
break;
case "TCM": case "TCM":
case "TCOM": case "TCOM":
builder.setComposer(value); builder.setComposer(value);
...@@ -148,4 +196,28 @@ public final class TextInformationFrame extends Id3Frame { ...@@ -148,4 +196,28 @@ public final class TextInformationFrame extends Id3Frame {
return new TextInformationFrame[size]; return new TextInformationFrame[size];
} }
}; };
// Private methods
private static List<Integer> parseId3v2point4TimestampFrameForDate(String value) {
// Timestamp string format is ISO-8601, can be `yyyy-MM-ddTHH:mm:ss`, or reduced precision
// at each point, for example `yyyy-MM` or `yyyy-MM-ddTHH:mm`.
List<Integer> dates = new ArrayList<>();
try {
if (value.length() >= 10) {
dates.add(Integer.parseInt(value.substring(0, 4)));
dates.add(Integer.parseInt(value.substring(5, 7)));
dates.add(Integer.parseInt(value.substring(8, 10)));
} else if (value.length() >= 7) {
dates.add(Integer.parseInt(value.substring(0, 4)));
dates.add(Integer.parseInt(value.substring(5, 7)));
} else if (value.length() >= 4) {
dates.add(Integer.parseInt(value.substring(0, 4)));
}
} catch (NumberFormatException e) {
// Invalid output, return.
return new ArrayList<>();
}
return dates;
}
} }
...@@ -54,7 +54,12 @@ public class MediaMetadataTest { ...@@ -54,7 +54,12 @@ public class MediaMetadataTest {
assertThat(mediaMetadata.totalTrackCount).isNull(); assertThat(mediaMetadata.totalTrackCount).isNull();
assertThat(mediaMetadata.folderType).isNull(); assertThat(mediaMetadata.folderType).isNull();
assertThat(mediaMetadata.isPlayable).isNull(); assertThat(mediaMetadata.isPlayable).isNull();
assertThat(mediaMetadata.year).isNull(); assertThat(mediaMetadata.recordingYear).isNull();
assertThat(mediaMetadata.recordingMonth).isNull();
assertThat(mediaMetadata.recordingDay).isNull();
assertThat(mediaMetadata.releaseYear).isNull();
assertThat(mediaMetadata.releaseMonth).isNull();
assertThat(mediaMetadata.releaseDay).isNull();
assertThat(mediaMetadata.composer).isNull(); assertThat(mediaMetadata.composer).isNull();
assertThat(mediaMetadata.conductor).isNull(); assertThat(mediaMetadata.conductor).isNull();
assertThat(mediaMetadata.writer).isNull(); assertThat(mediaMetadata.writer).isNull();
...@@ -105,7 +110,12 @@ public class MediaMetadataTest { ...@@ -105,7 +110,12 @@ public class MediaMetadataTest {
.setTotalTrackCount(12) .setTotalTrackCount(12)
.setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS) .setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS)
.setIsPlayable(true) .setIsPlayable(true)
.setYear(2000) .setRecordingYear(2000)
.setRecordingMonth(11)
.setRecordingDay(23)
.setReleaseYear(2001)
.setReleaseMonth(1)
.setReleaseDay(2)
.setComposer("Composer") .setComposer("Composer")
.setConductor("Conductor") .setConductor("Conductor")
.setWriter("Writer") .setWriter("Writer")
...@@ -126,7 +136,10 @@ public class MediaMetadataTest { ...@@ -126,7 +136,10 @@ public class MediaMetadataTest {
String albumTitle = "album title"; String albumTitle = "album title";
String albumArtist = "album Artist"; String albumArtist = "album Artist";
String trackNumberInfo = "11/17"; String trackNumberInfo = "11/17";
String year = "2000"; String recordingYear = "2000";
String recordingMonth = "07";
String recordingDay = "10";
String releaseDate = "2001-01-02T00:00:00";
String composer = "composer"; String composer = "composer";
String conductor = "conductor"; String conductor = "conductor";
String writer = "writer"; String writer = "writer";
...@@ -141,7 +154,14 @@ public class MediaMetadataTest { ...@@ -141,7 +154,14 @@ public class MediaMetadataTest {
/* id= */ "TP2", /* description= */ null, /* value= */ albumArtist), /* id= */ "TP2", /* description= */ null, /* value= */ albumArtist),
new TextInformationFrame( new TextInformationFrame(
/* id= */ "TRK", /* description= */ null, /* value= */ trackNumberInfo), /* id= */ "TRK", /* description= */ null, /* value= */ trackNumberInfo),
new TextInformationFrame(/* id= */ "TYE", /* description= */ null, /* value= */ year), new TextInformationFrame(
/* id= */ "TYE", /* description= */ null, /* value= */ recordingYear),
new TextInformationFrame(
/* id= */ "TDA",
/* description= */ null,
/* value= */ recordingDay + recordingMonth),
new TextInformationFrame(
/* id= */ "TDRL", /* description= */ null, /* value= */ releaseDate),
new TextInformationFrame( new TextInformationFrame(
/* id= */ "TCM", /* description= */ null, /* value= */ composer), /* id= */ "TCM", /* description= */ null, /* value= */ composer),
new TextInformationFrame( new TextInformationFrame(
...@@ -162,7 +182,12 @@ public class MediaMetadataTest { ...@@ -162,7 +182,12 @@ public class MediaMetadataTest {
assertThat(mediaMetadata.albumArtist.toString()).isEqualTo(albumArtist); assertThat(mediaMetadata.albumArtist.toString()).isEqualTo(albumArtist);
assertThat(mediaMetadata.trackNumber).isEqualTo(11); assertThat(mediaMetadata.trackNumber).isEqualTo(11);
assertThat(mediaMetadata.totalTrackCount).isEqualTo(17); assertThat(mediaMetadata.totalTrackCount).isEqualTo(17);
assertThat(mediaMetadata.year).isEqualTo(2000); assertThat(mediaMetadata.recordingYear).isEqualTo(2000);
assertThat(mediaMetadata.recordingMonth).isEqualTo(7);
assertThat(mediaMetadata.recordingDay).isEqualTo(10);
assertThat(mediaMetadata.releaseYear).isEqualTo(2001);
assertThat(mediaMetadata.releaseMonth).isEqualTo(1);
assertThat(mediaMetadata.releaseDay).isEqualTo(2);
assertThat(mediaMetadata.composer.toString()).isEqualTo(composer); assertThat(mediaMetadata.composer.toString()).isEqualTo(composer);
assertThat(mediaMetadata.conductor.toString()).isEqualTo(conductor); assertThat(mediaMetadata.conductor.toString()).isEqualTo(conductor);
assertThat(mediaMetadata.writer.toString()).isEqualTo(writer); assertThat(mediaMetadata.writer.toString()).isEqualTo(writer);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment