Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cb2e00ee authored by Nicolas Catania's avatar Nicolas Catania
Browse files

Impl. of the metadata getters.

In Metadata, implemented the method that unmarshall the data from
a metadata parcel an return java entities.

Add 2 new types of metadata (Boolean and Date) and fixed an issue
in TimedText: the w3c standard specify a duration as well as the
time to be played => added a duration field.

In MetadataParserTest.java:
Moved the util private methods at the bottom of the file.
Added new tests for the getters (GETTERS section)
parent 52e4ad89
Loading
Loading
Loading
Loading
+77 −31
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import android.graphics.Bitmap;
import android.os.Parcel;
import android.util.Log;

import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.HashMap;
import java.util.Set;
import java.util.TimeZone;


/**
@@ -104,13 +106,18 @@ public class Metadata

    public static final int STRING_VAL     = 1;
    public static final int INTEGER_VAL    = 2;
    public static final int LONG_VAL = 3;
    public static final int DOUBLE_VAL = 4;
    public static final int TIMED_TEXT_VAL = 5;
    private static final int LAST_TYPE = 5;
    public static final int BOOLEAN_VAL    = 3;
    public static final int LONG_VAL       = 4;
    public static final int DOUBLE_VAL     = 5;
    public static final int TIMED_TEXT_VAL = 6;
    public static final int DATE_VAL       = 7;
    public static final int BYTE_ARRAY_VAL = 8;
    // FIXME: misses a type for shared heap is missing (MemoryFile).
    // FIXME: misses a type for bitmaps.
    private static final int LAST_TYPE = 8;

    private static final String TAG = "media.Metadata";
    private static final int kMetaHeaderSize = 8;  // 8 bytes for the size + the marker
    private static final int kMetaHeaderSize = 8;  //  size + marker
    private static final int kMetaMarker = 0x4d455441;  // 'M' 'E' 'T' 'A'
    private static final int kRecordHeaderSize = 12; // size + id + type

@@ -122,21 +129,28 @@ public class Metadata
    // Used to look up if a key was present too.
    // Key: Metadata ID
    // Value: Offset of the metadata type field in the record.
    private final HashMap<Integer, Integer> mKeyToPosMap = new HashMap<Integer, Integer>();
    private final HashMap<Integer, Integer> mKeyToPosMap =
            new HashMap<Integer, Integer>();

    /**
     * Helper class to hold a pair (time, text). Can be used to implement caption.
     * Helper class to hold a triple (time, duration, text). Can be used to
     * implement caption.
     */
    public class TimedText {
        private Date mTime;
        private int mDuration;  // millisec
        private String mText;
        public TimedText(final Date time, final String text) {

        public TimedText(Date time, int duration, String text) {
            mTime = time;
            mDuration = duration;
            mText = text;
        }

        public String toString() {
            StringBuilder res = new StringBuilder(80);
            res.append(mTime).append(":").append(mText);
            res.append(mTime).append("-").append(mDuration)
                    .append(":").append(mText);
            return res.toString();
        }
    }
@@ -300,44 +314,64 @@ public class Metadata
        return mKeyToPosMap.containsKey(metadataId);
    }

    // Accessors
    // Accessors.
    // Caller must make sure the key is present using the {@code has}
    // method otherwise a RuntimeException will occur.

    public String getString(final int key) {
        // FIXME: Implement.
        return new String();
        checkType(key, STRING_VAL);
        return mParcel.readString();
    }

    public int getInt(final int key) {
        // FIXME: Implement.
        return 0;
        checkType(key, INTEGER_VAL);
        return mParcel.readInt();
    }

    public boolean getBoolean(final int key) {
        checkType(key, BOOLEAN_VAL);
        return mParcel.readInt() == 1;
    }

    public long getLong(final int key) {
        // FIXME: Implement.
        return 0;
        checkType(key, LONG_VAL);
        return mParcel.readLong();
    }

    public double getDouble(final int key) {
        // FIXME: Implement.
        return 0.0;
        checkType(key, DOUBLE_VAL);
        return mParcel.readDouble();
    }

    public byte[] getByteArray(final int key) {
        return new byte[0];
    }

    public Bitmap getBitmap(final int key) {
        // FIXME: Implement.
        return null;
        checkType(key, BYTE_ARRAY_VAL);
        return mParcel.createByteArray();
    }

    public Date getDate(final int key) {
        // FIXME: Implement.
        return new Date();
        checkType(key, DATE_VAL);
        final long timeSinceEpoch = mParcel.readLong();
        final String timeZone = mParcel.readString();

        if (timeZone.length() == 0) {
            return new Date(timeSinceEpoch);
        } else {
            TimeZone tz = TimeZone.getTimeZone(timeZone);
            Calendar cal = Calendar.getInstance(tz);

            cal.setTimeInMillis(timeSinceEpoch);
            return cal.getTime();
        }
    }

    public TimedText getTimedText(final int key) {
        // FIXME: Implement.
        return new TimedText(new Date(0), "<missing>");
        checkType(key, TIMED_TEXT_VAL);
        final Date startTime = new Date(mParcel.readLong());  // epoch
        final int duration = mParcel.readInt();  // millisec

        return new TimedText(startTime,
                             duration,
                             mParcel.readString());
    }

    // @return the last available system metadata id. Ids are
@@ -360,4 +394,16 @@ public class Metadata
        }
        return true;
    }

    // Check the type of the data match what is expected.
    private void checkType(final int key, final int expectedType) {
        final int pos = mKeyToPosMap.get(key);

        mParcel.setDataPosition(pos);

        final int type = mParcel.readInt();
        if (type != expectedType) {
            throw new IllegalStateException("Wrong type " + expectedType + " but got " + type);
        }
    }
}
+222 −10
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;

import java.util.Calendar;
import java.util.Date;

/*
 * Check the Java layer that parses serialized metadata in Parcel
 * works as expected.
@@ -89,16 +92,6 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase {
        mParcel.writeInt(kMarker);
    }

    // Insert a string record at the current position.
    private void writeStringRecord(int metadataId, String val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.STRING_VAL);
        mParcel.writeString(val);
        adjustSize(start);
    }

    // ----------------------------------------------------------------------
    // START OF THE TESTS

@@ -133,7 +126,9 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase {
        assertParse();
    }

    // ----------------------------------------------------------------------
    // RECORDS
    // ----------------------------------------------------------------------

    // A record header should be at least 12 bytes long
    @SmallTest
@@ -223,4 +218,221 @@ public class MediaPlayerMetadataParserTest extends AndroidTestCase {
        assertFalse(mMetadata.has(Metadata.GENRE));
        assertFalse(mMetadata.has(Metadata.firstCustomId()));
    }

    // ----------------------------------------------------------------------
    // GETTERS
    // ----------------------------------------------------------------------

    // getString
    @SmallTest
    public void testGetString() throws Exception {
        writeStringRecord(Metadata.TITLE, "a title");
        writeStringRecord(Metadata.GENRE, "comedy");
        adjustSize();
        assertParse();

        assertEquals("a title", mMetadata.getString(Metadata.TITLE));
        assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
    }

    // get an empty string.
    @SmallTest
    public void testGetEmptyString() throws Exception {
        writeStringRecord(Metadata.TITLE, "");
        adjustSize();
        assertParse();

        assertEquals("", mMetadata.getString(Metadata.TITLE));
    }

    // get a string when a NULL value was in the parcel
    @SmallTest
    public void testGetNullString() throws Exception {
        writeStringRecord(Metadata.TITLE, null);
        adjustSize();
        assertParse();

        assertEquals(null, mMetadata.getString(Metadata.TITLE));
    }

    // get a string when an integer is actually present
    @SmallTest
    public void testWrongType() throws Exception {
        writeIntRecord(Metadata.DURATION, 5);
        adjustSize();
        assertParse();

        try {
            mMetadata.getString(Metadata.DURATION);
        } catch (IllegalStateException ise) {
            return;
        }
        fail("Exception was not thrown");
    }

    // getInt
    @SmallTest
    public void testGetInt() throws Exception {
        writeIntRecord(Metadata.CD_TRACK_NUM, 1);
        adjustSize();
        assertParse();

        assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
    }

    // getBoolean
    @SmallTest
    public void testGetBoolean() throws Exception {
        writeBooleanRecord(Metadata.DRM_CRIPPLED, true);
        adjustSize();
        assertParse();

        assertEquals(true, mMetadata.getBoolean(Metadata.DRM_CRIPPLED));
    }

    // getLong
    @SmallTest
    public void testGetLong() throws Exception {
        writeLongRecord(Metadata.DURATION, 1L);
        adjustSize();
        assertParse();

        assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
    }

    // getDouble
    @SmallTest
    public void testGetDouble() throws Exception {
        writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
        adjustSize();
        assertParse();

        assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
    }

    // getByteArray
    @SmallTest
    public void testGetByteArray() throws Exception {
        byte data[] = new byte[]{1,2,3,4,5};

        writeByteArrayRecord(Metadata.ALBUM_ART, data);
        adjustSize();
        assertParse();

        byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
        for (int i = 0; i < data.length; ++i) {
            assertEquals(data[i], res[i]);
        }
    }

    // getDate
    @SmallTest
    public void testGetDate() throws Exception {
        writeDateRecord(Metadata.DATE, 0, "PST");
        adjustSize();
        assertParse();

        assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
    }

    // getTimedText
    @SmallTest
    public void testGetTimedText() throws Exception {
        Date now = Calendar.getInstance().getTime();
        writeTimedTextRecord(Metadata.CAPTION, now.getTime(),
                             10, "Some caption");
        adjustSize();
        assertParse();

        Metadata.TimedText caption = mMetadata.getTimedText(Metadata.CAPTION);
        assertEquals("" + now + "-" + 10 + ":Some caption", caption.toString());
    }

    // ----------------------------------------------------------------------
    // HELPERS TO APPEND RECORDS
    // ----------------------------------------------------------------------

    // Insert a string record at the current position.
    private void writeStringRecord(int metadataId, String val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.STRING_VAL);
        mParcel.writeString(val);
        adjustSize(start);
    }

    // Insert an int record at the current position.
    private void writeIntRecord(int metadataId, int val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.INTEGER_VAL);
        mParcel.writeInt(val);
        adjustSize(start);
    }

    // Insert a boolean record at the current position.
    private void writeBooleanRecord(int metadataId, boolean val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.BOOLEAN_VAL);
        mParcel.writeInt(val ? 1 : 0);
        adjustSize(start);
    }

    // Insert a Long record at the current position.
    private void writeLongRecord(int metadataId, long val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.LONG_VAL);
        mParcel.writeLong(val);
        adjustSize(start);
    }

    // Insert a Double record at the current position.
    private void writeDoubleRecord(int metadataId, double val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.DOUBLE_VAL);
        mParcel.writeDouble(val);
        adjustSize(start);
    }

    // Insert a ByteArray record at the current position.
    private void writeByteArrayRecord(int metadataId, byte[] val) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
        mParcel.writeByteArray(val);
        adjustSize(start);
    }

    // Insert a Date record at the current position.
    private void writeDateRecord(int metadataId, long time, String tz) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.DATE_VAL);
        mParcel.writeLong(time);
        mParcel.writeString(tz);
        adjustSize(start);
    }

    // Insert a TimedText record at the current position.
    private void writeTimedTextRecord(int metadataId, long begin,
                                      int duration, String text) {
        final int start = mParcel.dataPosition();
        mParcel.writeInt(-1);  // Placeholder for the length
        mParcel.writeInt(metadataId);
        mParcel.writeInt(Metadata.TIMED_TEXT_VAL);
        mParcel.writeLong(begin);
        mParcel.writeInt(duration);
        mParcel.writeString(text);
        adjustSize(start);
    }
}