blob: a055c8bac874739f1b3b8463f7151822feeb1962 [file] [log] [blame]
/*
* Copyright (C) 2011 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 android.media;
import android.os.Parcel;
import android.util.Log;
import java.util.HashMap;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
/**
* Class to hold the timed text's metadata.
*
* {@hide}
*/
public class TimedText
{
private static final int FIRST_PUBLIC_KEY = 1;
// These keys must be in sync with the keys in TextDescription.h
public static final int KEY_DISPLAY_FLAGS = 1; // int
public static final int KEY_STYLE_FLAGS = 2; // int
public static final int KEY_BACKGROUND_COLOR_RGBA = 3; // int
public static final int KEY_HIGHLIGHT_COLOR_RGBA = 4; // int
public static final int KEY_SCROLL_DELAY = 5; // int
public static final int KEY_WRAP_TEXT = 6; // int
public static final int KEY_START_TIME = 7; // int
public static final int KEY_STRUCT_BLINKING_TEXT_LIST = 8; // List<CharPos>
public static final int KEY_STRUCT_FONT_LIST = 9; // List<Font>
public static final int KEY_STRUCT_HIGHLIGHT_LIST = 10; // List<CharPos>
public static final int KEY_STRUCT_HYPER_TEXT_LIST = 11; // List<HyperText>
public static final int KEY_STRUCT_KARAOKE_LIST = 12; // List<Karaoke>
public static final int KEY_STRUCT_STYLE_LIST = 13; // List<Style>
public static final int KEY_STRUCT_TEXT_POS = 14; // TextPos
public static final int KEY_STRUCT_JUSTIFICATION = 15; // Justification
public static final int KEY_STRUCT_TEXT = 16; // Text
private static final int LAST_PUBLIC_KEY = 16;
private static final int FIRST_PRIVATE_KEY = 101;
// The following keys are used between TimedText.java and
// TextDescription.cpp in order to parce the Parcel.
private static final int KEY_GLOBAL_SETTING = 101;
private static final int KEY_LOCAL_SETTING = 102;
private static final int KEY_START_CHAR = 103;
private static final int KEY_END_CHAR = 104;
private static final int KEY_FONT_ID = 105;
private static final int KEY_FONT_SIZE = 106;
private static final int KEY_TEXT_COLOR_RGBA = 107;
private static final int LAST_PRIVATE_KEY = 107;
private static final String TAG = "TimedText";
private Parcel mParcel = Parcel.obtain();
private final HashMap<Integer, Object> mKeyObjectMap =
new HashMap<Integer, Object>();
private int mDisplayFlags = -1;
private int mBackgroundColorRGBA = -1;
private int mHighlightColorRGBA = -1;
private int mScrollDelay = -1;
private int mWrapText = -1;
private List<CharPos> mBlinkingPosList = null;
private List<CharPos> mHighlightPosList = null;
private List<Karaoke> mKaraokeList = null;
private List<Font> mFontList = null;
private List<Style> mStyleList = null;
private List<HyperText> mHyperTextList = null;
private TextPos mTextPos;
private Justification mJustification;
private Text mTextStruct;
/**
* Helper class to hold the text length and text content of
* one text sample. The member variables in this class are
* read-only.
*/
public class Text {
/**
* The byte-count of this text sample
*/
public int textLen;
/**
* The text sample
*/
public byte[] text;
public Text() { }
}
/**
* Helper class to hold the start char offset and end char offset
* for Blinking Text or Highlight Text. endChar is the end offset
* of the text (startChar + number of characters to be highlighted
* or blinked). The member variables in this class are read-only.
*/
public class CharPos {
/**
* The offset of the start character
*/
public int startChar = -1;
/**
* The offset of the end character
*/
public int endChar = -1;
public CharPos() { }
}
/**
* Helper class to hold the box position to display the text sample.
* The member variables in this class are read-only.
*/
public class TextPos {
/**
* The top position of the text
*/
public int top = -1;
/**
* The left position of the text
*/
public int left = -1;
/**
* The bottom position of the text
*/
public int bottom = -1;
/**
* The right position of the text
*/
public int right = -1;
public TextPos() { }
}
/**
* Helper class to hold the justification for text display in the text box.
* The member variables in this class are read-only.
*/
public class Justification {
/**
* horizontalJustification 0: left, 1: centered, -1: right
*/
public int horizontalJustification = -1;
/**
* verticalJustification 0: top, 1: centered, -1: bottom
*/
public int verticalJustification = -1;
public Justification() { }
}
/**
* Helper class to hold the style information to display the text.
* The member variables in this class are read-only.
*/
public class Style {
/**
* The offset of the start character which applys this style
*/
public int startChar = -1;
/**
* The offset of the end character which applys this style
*/
public int endChar = -1;
/**
* ID of the font. This ID will be used to choose the font
* to be used from the font list.
*/
public int fontID = -1;
/**
* True if the characters should be bold
*/
public boolean isBold = false;
/**
* True if the characters should be italic
*/
public boolean isItalic = false;
/**
* True if the characters should be underlined
*/
public boolean isUnderlined = false;
/**
* The size of the font
*/
public int fontSize = -1;
/**
* To specify the RGBA color: 8 bits each of red, green, blue,
* and an alpha(transparency) value
*/
public int colorRGBA = -1;
public Style() { }
}
/**
* Helper class to hold the font ID and name.
* The member variables in this class are read-only.
*/
public class Font {
/**
* The font ID
*/
public int ID = -1;
/**
* The font name
*/
public String name;
public Font() { }
}
/**
* Helper class to hold the karaoke information.
* The member variables in this class are read-only.
*/
public class Karaoke {
/**
* The start time (in milliseconds) to highlight the characters
* specified by startChar and endChar.
*/
public int startTimeMs = -1;
/**
* The end time (in milliseconds) to highlight the characters
* specified by startChar and endChar.
*/
public int endTimeMs = -1;
/**
* The offset of the start character to be highlighted
*/
public int startChar = -1;
/**
* The offset of the end character to be highlighted
*/
public int endChar = -1;
public Karaoke() { }
}
/**
* Helper class to hold the hyper text information.
* The member variables in this class are read-only.
*/
public class HyperText {
/**
* The offset of the start character
*/
public int startChar = -1;
/**
* The offset of the end character
*/
public int endChar = -1;
/**
* The linked-to URL
*/
public String URL;
/**
* The "alt" string for user display
*/
public String altString;
public HyperText() { }
}
/**
* @param obj the byte array which contains the timed text.
* @throws IllegalArgumentExcept if parseParcel() fails.
* {@hide}
*/
public TimedText(byte[] obj) {
mParcel.unmarshall(obj, 0, obj.length);
if (!parseParcel()) {
mKeyObjectMap.clear();
throw new IllegalArgumentException("parseParcel() fails");
}
}
/**
* Go over all the records, collecting metadata keys and fields in the
* Parcel. These are stored in mKeyObjectMap for application to retrieve.
* @return false if an error occurred during parsing. Otherwise, true.
*/
private boolean parseParcel() {
mParcel.setDataPosition(0);
if (mParcel.dataAvail() == 0) {
return false;
}
int type = mParcel.readInt();
if (type == KEY_LOCAL_SETTING) {
type = mParcel.readInt();
if (type != KEY_START_TIME) {
return false;
}
int mStartTimeMs = mParcel.readInt();
mKeyObjectMap.put(type, mStartTimeMs);
type = mParcel.readInt();
if (type != KEY_STRUCT_TEXT) {
return false;
}
mTextStruct = new Text();
mTextStruct.textLen = mParcel.readInt();
mTextStruct.text = mParcel.createByteArray();
mKeyObjectMap.put(type, mTextStruct);
} else if (type != KEY_GLOBAL_SETTING) {
Log.w(TAG, "Invalid timed text key found: " + type);
return false;
}
while (mParcel.dataAvail() > 0) {
int key = mParcel.readInt();
if (!isValidKey(key)) {
Log.w(TAG, "Invalid timed text key found: " + key);
return false;
}
Object object = null;
switch (key) {
case KEY_STRUCT_STYLE_LIST: {
readStyle();
object = mStyleList;
break;
}
case KEY_STRUCT_FONT_LIST: {
readFont();
object = mFontList;
break;
}
case KEY_STRUCT_HIGHLIGHT_LIST: {
readHighlight();
object = mHighlightPosList;
break;
}
case KEY_STRUCT_KARAOKE_LIST: {
readKaraoke();
object = mKaraokeList;
break;
}
case KEY_STRUCT_HYPER_TEXT_LIST: {
readHyperText();
object = mHyperTextList;
break;
}
case KEY_STRUCT_BLINKING_TEXT_LIST: {
readBlinkingText();
object = mBlinkingPosList;
break;
}
case KEY_WRAP_TEXT: {
mWrapText = mParcel.readInt();
object = mWrapText;
break;
}
case KEY_HIGHLIGHT_COLOR_RGBA: {
mHighlightColorRGBA = mParcel.readInt();
object = mHighlightColorRGBA;
break;
}
case KEY_DISPLAY_FLAGS: {
mDisplayFlags = mParcel.readInt();
object = mDisplayFlags;
break;
}
case KEY_STRUCT_JUSTIFICATION: {
mJustification = new Justification();
mJustification.horizontalJustification = mParcel.readInt();
mJustification.verticalJustification = mParcel.readInt();
object = mJustification;
break;
}
case KEY_BACKGROUND_COLOR_RGBA: {
mBackgroundColorRGBA = mParcel.readInt();
object = mBackgroundColorRGBA;
break;
}
case KEY_STRUCT_TEXT_POS: {
mTextPos = new TextPos();
mTextPos.top = mParcel.readInt();
mTextPos.left = mParcel.readInt();
mTextPos.bottom = mParcel.readInt();
mTextPos.right = mParcel.readInt();
object = mTextPos;
break;
}
case KEY_SCROLL_DELAY: {
mScrollDelay = mParcel.readInt();
object = mScrollDelay;
break;
}
default: {
break;
}
}
if (object != null) {
if (mKeyObjectMap.containsKey(key)) {
mKeyObjectMap.remove(key);
}
mKeyObjectMap.put(key, object);
}
}
mParcel.recycle();
return true;
}
/**
* To parse and store the Style list.
*/
private void readStyle() {
Style style = new Style();
boolean endOfStyle = false;
while (!endOfStyle && (mParcel.dataAvail() > 0)) {
int key = mParcel.readInt();
switch (key) {
case KEY_START_CHAR: {
style.startChar = mParcel.readInt();
break;
}
case KEY_END_CHAR: {
style.endChar = mParcel.readInt();
break;
}
case KEY_FONT_ID: {
style.fontID = mParcel.readInt();
break;
}
case KEY_STYLE_FLAGS: {
int flags = mParcel.readInt();
// In the absence of any bits set in flags, the text
// is plain. Otherwise, 1: bold, 2: italic, 4: underline
style.isBold = ((flags % 2) == 1);
style.isItalic = ((flags % 4) >= 2);
style.isUnderlined = ((flags / 4) == 1);
break;
}
case KEY_FONT_SIZE: {
style.fontSize = mParcel.readInt();
break;
}
case KEY_TEXT_COLOR_RGBA: {
style.colorRGBA = mParcel.readInt();
break;
}
default: {
// End of the Style parsing. Reset the data position back
// to the position before the last mParcel.readInt() call.
mParcel.setDataPosition(mParcel.dataPosition() - 4);
endOfStyle = true;
break;
}
}
}
if (mStyleList == null) {
mStyleList = new ArrayList<Style>();
}
mStyleList.add(style);
}
/**
* To parse and store the Font list
*/
private void readFont() {
int entryCount = mParcel.readInt();
for (int i = 0; i < entryCount; i++) {
Font font = new Font();
font.ID = mParcel.readInt();
int nameLen = mParcel.readInt();
byte[] text = mParcel.createByteArray();
font.name = new String(text, 0, nameLen);
if (mFontList == null) {
mFontList = new ArrayList<Font>();
}
mFontList.add(font);
}
}
/**
* To parse and store the Highlight list
*/
private void readHighlight() {
CharPos pos = new CharPos();
pos.startChar = mParcel.readInt();
pos.endChar = mParcel.readInt();
if (mHighlightPosList == null) {
mHighlightPosList = new ArrayList<CharPos>();
}
mHighlightPosList.add(pos);
}
/**
* To parse and store the Karaoke list
*/
private void readKaraoke() {
int entryCount = mParcel.readInt();
for (int i = 0; i < entryCount; i++) {
Karaoke kara = new Karaoke();
kara.startTimeMs = mParcel.readInt();
kara.endTimeMs = mParcel.readInt();
kara.startChar = mParcel.readInt();
kara.endChar = mParcel.readInt();
if (mKaraokeList == null) {
mKaraokeList = new ArrayList<Karaoke>();
}
mKaraokeList.add(kara);
}
}
/**
* To parse and store HyperText list
*/
private void readHyperText() {
HyperText hyperText = new HyperText();
hyperText.startChar = mParcel.readInt();
hyperText.endChar = mParcel.readInt();
int len = mParcel.readInt();
byte[] url = mParcel.createByteArray();
hyperText.URL = new String(url, 0, len);
len = mParcel.readInt();
byte[] alt = mParcel.createByteArray();
hyperText.altString = new String(alt, 0, len);
if (mHyperTextList == null) {
mHyperTextList = new ArrayList<HyperText>();
}
mHyperTextList.add(hyperText);
}
/**
* To parse and store blinking text list
*/
private void readBlinkingText() {
CharPos blinkingPos = new CharPos();
blinkingPos.startChar = mParcel.readInt();
blinkingPos.endChar = mParcel.readInt();
if (mBlinkingPosList == null) {
mBlinkingPosList = new ArrayList<CharPos>();
}
mBlinkingPosList.add(blinkingPos);
}
/**
* To check whether the given key is valid.
* @param key the key to be checked.
* @return true if the key is a valid one. Otherwise, false.
*/
public boolean isValidKey(final int key) {
if (!((key >= FIRST_PUBLIC_KEY) && (key <= LAST_PUBLIC_KEY))
&& !((key >= FIRST_PRIVATE_KEY) && (key <= LAST_PRIVATE_KEY))) {
return false;
}
return true;
}
/**
* To check whether the given key is contained in this TimedText object.
* @param key the key to be checked.
* @return true if the key is contained in this TimedText object.
* Otherwise, false.
*/
public boolean containsKey(final int key) {
if (isValidKey(key) && mKeyObjectMap.containsKey(key)) {
return true;
}
return false;
}
/**
* @return a set of the keys contained in this TimedText object.
*/
public Set keySet() {
return mKeyObjectMap.keySet();
}
/**
* To retrieve the object associated with the key. Caller must make sure
* the key is present using the containsKey method otherwise a
* RuntimeException will occur.
* @param key the key used to retrieve the object.
* @return an object. The object could be an instanceof Integer, List, or
* any of the helper classes such as TextPos, Justification, and Text.
*/
public Object getObject(final int key) {
if (containsKey(key)) {
return mKeyObjectMap.get(key);
} else {
throw new IllegalArgumentException("Invalid key: " + key);
}
}
}