Fix some categories of error prone warnings

When switching from Stack to ArrayDeque, calls to add() need to be replaced by
calls to push() because ArrayDeque treats the first element in the list as the
top of the stack.

String.split() has counterintuitive default behavior; see
https://github.com/google/error-prone/blob/master/docs/bugpattern/StringSplitter.md.
I've switched usages to pass limit = -1 argument, which means empty elements are
no longer removed from the end of the returned array.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=199472592
This commit is contained in:
andrewlewis 2018-06-06 08:55:07 -07:00 committed by Oliver Woodman
parent 879f689488
commit ef8a47ba7d
35 changed files with 169 additions and 77 deletions

View File

@ -1543,6 +1543,7 @@ import java.util.Collections;
} }
} }
@SuppressWarnings("ParameterNotNullable")
private void updatePlayingPeriodRenderers(@Nullable MediaPeriodHolder oldPlayingPeriodHolder) private void updatePlayingPeriodRenderers(@Nullable MediaPeriodHolder oldPlayingPeriodHolder)
throws ExoPlaybackException { throws ExoPlaybackException {
MediaPeriodHolder newPlayingPeriodHolder = queue.getPlayingPeriod(); MediaPeriodHolder newPlayingPeriodHolder = queue.getPlayingPeriod();

View File

@ -17,7 +17,7 @@ package com.google.android.exoplayer2.decoder;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.util.LinkedList; import java.util.ArrayDeque;
/** /**
* Base class for {@link Decoder}s that use their own decode thread. * Base class for {@link Decoder}s that use their own decode thread.
@ -28,8 +28,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
private final Thread decodeThread; private final Thread decodeThread;
private final Object lock; private final Object lock;
private final LinkedList<I> queuedInputBuffers; private final ArrayDeque<I> queuedInputBuffers;
private final LinkedList<O> queuedOutputBuffers; private final ArrayDeque<O> queuedOutputBuffers;
private final I[] availableInputBuffers; private final I[] availableInputBuffers;
private final O[] availableOutputBuffers; private final O[] availableOutputBuffers;
@ -48,8 +48,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
*/ */
protected SimpleDecoder(I[] inputBuffers, O[] outputBuffers) { protected SimpleDecoder(I[] inputBuffers, O[] outputBuffers) {
lock = new Object(); lock = new Object();
queuedInputBuffers = new LinkedList<>(); queuedInputBuffers = new ArrayDeque<>();
queuedOutputBuffers = new LinkedList<>(); queuedOutputBuffers = new ArrayDeque<>();
availableInputBuffers = inputBuffers; availableInputBuffers = inputBuffers;
availableInputBufferCount = inputBuffers.length; availableInputBufferCount = inputBuffers.length;
for (int i = 0; i < availableInputBufferCount; i++) { for (int i = 0; i < availableInputBufferCount; i++) {

View File

@ -108,7 +108,8 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
@Override @Override
public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException { public byte[] executeProvisionRequest(UUID uuid, ProvisionRequest request) throws IOException {
String url = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData()); String url =
request.getDefaultUrl() + "&signedRequest=" + Util.fromUtf8Bytes(request.getData());
return executePost(dataSourceFactory, url, new byte[0], null); return executePost(dataSourceFactory, url, new byte[0], null);
} }

View File

@ -24,7 +24,7 @@ import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Stack; import java.util.ArrayDeque;
/** /**
* Default implementation of {@link EbmlReader}. * Default implementation of {@link EbmlReader}.
@ -46,15 +46,21 @@ import java.util.Stack;
private static final int VALID_FLOAT32_ELEMENT_SIZE_BYTES = 4; private static final int VALID_FLOAT32_ELEMENT_SIZE_BYTES = 4;
private static final int VALID_FLOAT64_ELEMENT_SIZE_BYTES = 8; private static final int VALID_FLOAT64_ELEMENT_SIZE_BYTES = 8;
private final byte[] scratch = new byte[8]; private final byte[] scratch;
private final Stack<MasterElement> masterElementsStack = new Stack<>(); private final ArrayDeque<MasterElement> masterElementsStack;
private final VarintReader varintReader = new VarintReader(); private final VarintReader varintReader;
private EbmlReaderOutput output; private EbmlReaderOutput output;
private @ElementState int elementState; private @ElementState int elementState;
private int elementId; private int elementId;
private long elementContentSize; private long elementContentSize;
public DefaultEbmlReader() {
scratch = new byte[8];
masterElementsStack = new ArrayDeque<>();
varintReader = new VarintReader();
}
@Override @Override
public void init(EbmlReaderOutput eventHandler) { public void init(EbmlReaderOutput eventHandler) {
this.output = eventHandler; this.output = eventHandler;
@ -100,7 +106,7 @@ import java.util.Stack;
case EbmlReaderOutput.TYPE_MASTER: case EbmlReaderOutput.TYPE_MASTER:
long elementContentPosition = input.getPosition(); long elementContentPosition = input.getPosition();
long elementEndPosition = elementContentPosition + elementContentSize; long elementEndPosition = elementContentPosition + elementContentSize;
masterElementsStack.add(new MasterElement(elementId, elementEndPosition)); masterElementsStack.push(new MasterElement(elementId, elementEndPosition));
output.startMasterElement(elementId, elementContentPosition, elementContentSize); output.startMasterElement(elementId, elementContentPosition, elementContentSize);
elementState = ELEMENT_STATE_READ_ID; elementState = ELEMENT_STATE_READ_ID;
return true; return true;

View File

@ -78,8 +78,9 @@ import java.io.IOException;
return false; return false;
} }
if (size != 0) { if (size != 0) {
input.advancePeekPosition((int) size); int sizeInt = (int) size;
peekLength += size; input.advancePeekPosition(sizeInt);
peekLength += sizeInt;
} }
} }
return peekLength == headerStart + headerSize; return peekLength == headerStart + headerSize;

View File

@ -108,4 +108,7 @@ import com.google.android.exoplayer2.util.Util;
return new Results(offsets, sizes, maximumSize, timestamps, flags, duration); return new Results(offsets, sizes, maximumSize, timestamps, flags, duration);
} }
private FixedSampleSizeRechunker() {
// Prevent instantiation.
}
} }

View File

@ -50,7 +50,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Stack;
import java.util.UUID; import java.util.UUID;
/** /**
@ -141,7 +140,7 @@ public final class FragmentedMp4Extractor implements Extractor {
// Parser state. // Parser state.
private final ParsableByteArray atomHeader; private final ParsableByteArray atomHeader;
private final byte[] extendedTypeScratch; private final byte[] extendedTypeScratch;
private final Stack<ContainerAtom> containerAtoms; private final ArrayDeque<ContainerAtom> containerAtoms;
private final ArrayDeque<MetadataSampleInfo> pendingMetadataSampleInfos; private final ArrayDeque<MetadataSampleInfo> pendingMetadataSampleInfos;
private final @Nullable TrackOutput additionalEmsgTrackOutput; private final @Nullable TrackOutput additionalEmsgTrackOutput;
@ -257,7 +256,7 @@ public final class FragmentedMp4Extractor implements Extractor {
nalPrefix = new ParsableByteArray(5); nalPrefix = new ParsableByteArray(5);
nalBuffer = new ParsableByteArray(); nalBuffer = new ParsableByteArray();
extendedTypeScratch = new byte[16]; extendedTypeScratch = new byte[16];
containerAtoms = new Stack<>(); containerAtoms = new ArrayDeque<>();
pendingMetadataSampleInfos = new ArrayDeque<>(); pendingMetadataSampleInfos = new ArrayDeque<>();
trackBundles = new SparseArray<>(); trackBundles = new SparseArray<>();
durationUs = C.TIME_UNSET; durationUs = C.TIME_UNSET;
@ -390,7 +389,7 @@ public final class FragmentedMp4Extractor implements Extractor {
if (shouldParseContainerAtom(atomType)) { if (shouldParseContainerAtom(atomType)) {
long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE; long endPosition = input.getPosition() + atomSize - Atom.HEADER_SIZE;
containerAtoms.add(new ContainerAtom(atomType, endPosition)); containerAtoms.push(new ContainerAtom(atomType, endPosition));
if (atomSize == atomHeaderBytesRead) { if (atomSize == atomHeaderBytesRead) {
processAtomEnded(endPosition); processAtomEnded(endPosition);
} else { } else {

View File

@ -37,9 +37,9 @@ import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Stack;
/** /**
* Extracts data from the MP4 container format. * Extracts data from the MP4 container format.
@ -101,7 +101,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private final ParsableByteArray nalLength; private final ParsableByteArray nalLength;
private final ParsableByteArray atomHeader; private final ParsableByteArray atomHeader;
private final Stack<ContainerAtom> containerAtoms; private final ArrayDeque<ContainerAtom> containerAtoms;
@State private int parserState; @State private int parserState;
private int atomType; private int atomType;
@ -137,7 +137,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
public Mp4Extractor(@Flags int flags) { public Mp4Extractor(@Flags int flags) {
this.flags = flags; this.flags = flags;
atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE); atomHeader = new ParsableByteArray(Atom.LONG_HEADER_SIZE);
containerAtoms = new Stack<>(); containerAtoms = new ArrayDeque<>();
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE); nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
nalLength = new ParsableByteArray(4); nalLength = new ParsableByteArray(4);
sampleTrackIndex = C.INDEX_UNSET; sampleTrackIndex = C.INDEX_UNSET;
@ -303,7 +303,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
if (shouldParseContainerAtom(atomType)) { if (shouldParseContainerAtom(atomType)) {
long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead; long endPosition = input.getPosition() + atomSize - atomHeaderBytesRead;
containerAtoms.add(new ContainerAtom(atomType, endPosition)); containerAtoms.push(new ContainerAtom(atomType, endPosition));
if (atomSize == atomHeaderBytesRead) { if (atomSize == atomHeaderBytesRead) {
processAtomEnded(endPosition); processAtomEnded(endPosition);
} else { } else {

View File

@ -49,6 +49,7 @@ public final class PsshAtomUtil {
* @param data The scheme specific data. * @param data The scheme specific data.
* @return The PSSH atom. * @return The PSSH atom.
*/ */
@SuppressWarnings("ParameterNotNullable")
public static byte[] buildPsshAtom( public static byte[] buildPsshAtom(
UUID systemId, @Nullable UUID[] keyIds, @Nullable byte[] data) { UUID systemId, @Nullable UUID[] keyIds, @Nullable byte[] data) {
boolean buildV1Atom = keyIds != null; boolean buildV1Atom = keyIds != null;

View File

@ -130,6 +130,6 @@ import java.util.List;
} else { } else {
length = 10000 << length; length = 10000 << length;
} }
return frames * length; return (long) frames * length;
} }
} }

View File

@ -357,12 +357,12 @@ import java.util.Arrays;
for (int i = 0; i < lengthMap.length; i++) { for (int i = 0; i < lengthMap.length; i++) {
if (isSparse) { if (isSparse) {
if (bitArray.readBit()) { if (bitArray.readBit()) {
lengthMap[i] = bitArray.readBits(5) + 1; lengthMap[i] = (long) (bitArray.readBits(5) + 1);
} else { // entry unused } else { // entry unused
lengthMap[i] = 0; lengthMap[i] = 0;
} }
} else { // not sparse } else { // not sparse
lengthMap[i] = bitArray.readBits(5) + 1; lengthMap[i] = (long) (bitArray.readBits(5) + 1);
} }
} }
} else { } else {
@ -392,7 +392,7 @@ import java.util.Arrays;
lookupValuesCount = 0; lookupValuesCount = 0;
} }
} else { } else {
lookupValuesCount = entries * dimensions; lookupValuesCount = (long) entries * dimensions;
} }
// discard (no decoding required yet) // discard (no decoding required yet)
bitArray.skipBits((int) (lookupValuesCount * valueBits)); bitArray.skipBits((int) (lookupValuesCount * valueBits));
@ -407,6 +407,10 @@ import java.util.Arrays;
return (long) Math.floor(Math.pow(entries, 1.d / dimension)); return (long) Math.floor(Math.pow(entries, 1.d / dimension));
} }
private VorbisUtil() {
// Prevent instantiation.
}
public static final class CodeBook { public static final class CodeBook {
public final int dimensions; public final int dimensions;

View File

@ -25,7 +25,7 @@ import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
/** Reads a {@code WavHeader} from an input stream; supports resuming from input failures. */ /** Reads a {@code WavHeader} from an input stream; supports resuming from input failures. */
/*package*/ final class WavHeaderReader { /* package */ final class WavHeaderReader {
private static final String TAG = "WavHeaderReader"; private static final String TAG = "WavHeaderReader";
@ -158,6 +158,10 @@ import java.io.IOException;
wavHeader.setDataBounds(input.getPosition(), chunkHeader.size); wavHeader.setDataBounds(input.getPosition(), chunkHeader.size);
} }
private WavHeaderReader() {
// Prevent instantiation.
}
/** Container for a WAV chunk header. */ /** Container for a WAV chunk header. */
private static final class ChunkHeader { private static final class ChunkHeader {

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package com.google.android.exoplayer2.offline; package com.google.android.exoplayer2.offline;
import android.net.Uri; import android.net.Uri;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;

View File

@ -201,6 +201,8 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
throws InterruptedException, IOException; throws InterruptedException, IOException;
/** Initializes the download, returning a list of {@link Segment}s that need to be downloaded. */ /** Initializes the download, returning a list of {@link Segment}s that need to be downloaded. */
// Writes to downloadedSegments and downloadedBytes are safe. See the comment on download().
@SuppressWarnings("NonAtomicVolatileUpdate")
private List<Segment> initDownload() throws IOException, InterruptedException { private List<Segment> initDownload() throws IOException, InterruptedException {
M manifest = getManifest(dataSource, manifestUri); M manifest = getManifest(dataSource, manifestUri);
if (!streamKeys.isEmpty()) { if (!streamKeys.isEmpty()) {

View File

@ -72,11 +72,14 @@ public final class TrackGroup implements Parcelable {
} }
/** /**
* Returns the index of the track with the given format in the group. * Returns the index of the track with the given format in the group. The format is located by
* identity so, for example, {@code group.indexOf(group.getFormat(index)) == index} even if
* multiple tracks have formats that contain the same values.
* *
* @param format The format. * @param format The format.
* @return The index of the track, or {@link C#INDEX_UNSET} if no such track exists. * @return The index of the track, or {@link C#INDEX_UNSET} if no such track exists.
*/ */
@SuppressWarnings("ReferenceEquality")
public int indexOf(Format format) { public int indexOf(Format format) {
for (int i = 0; i < formats.length; i++) { for (int i = 0; i < formats.length; i++) {
if (format == formats[i]) { if (format == formats[i]) {

View File

@ -38,7 +38,6 @@ import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList;
import java.util.List; import java.util.List;
/** /**
@ -196,7 +195,10 @@ public final class Cea708Decoder extends CeaDecoder {
@Override @Override
protected void decode(SubtitleInputBuffer inputBuffer) { protected void decode(SubtitleInputBuffer inputBuffer) {
ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit()); // Subtitle input buffers are non-direct and the position is zero, so calling array() is safe.
@SuppressWarnings("ByteBufferBackingArray")
byte[] inputBufferData = inputBuffer.data.array();
ccData.reset(inputBufferData, inputBuffer.data.limit());
while (ccData.bytesLeft() >= 3) { while (ccData.bytesLeft() >= 3) {
int ccTypeAndValid = (ccData.readUnsignedByte() & 0x07); int ccTypeAndValid = (ccData.readUnsignedByte() & 0x07);
@ -879,7 +881,7 @@ public final class Cea708Decoder extends CeaDecoder {
private int row; private int row;
public CueBuilder() { public CueBuilder() {
rolledUpCaptions = new LinkedList<>(); rolledUpCaptions = new ArrayList<>();
captionStringBuilder = new SpannableStringBuilder(); captionStringBuilder = new SpannableStringBuilder();
reset(); reset();
} }

View File

@ -24,7 +24,7 @@ import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.text.SubtitleInputBuffer; import com.google.android.exoplayer2.text.SubtitleInputBuffer;
import com.google.android.exoplayer2.text.SubtitleOutputBuffer; import com.google.android.exoplayer2.text.SubtitleOutputBuffer;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import java.util.LinkedList; import java.util.ArrayDeque;
import java.util.PriorityQueue; import java.util.PriorityQueue;
/** /**
@ -35,8 +35,8 @@ import java.util.PriorityQueue;
private static final int NUM_INPUT_BUFFERS = 10; private static final int NUM_INPUT_BUFFERS = 10;
private static final int NUM_OUTPUT_BUFFERS = 2; private static final int NUM_OUTPUT_BUFFERS = 2;
private final LinkedList<CeaInputBuffer> availableInputBuffers; private final ArrayDeque<CeaInputBuffer> availableInputBuffers;
private final LinkedList<SubtitleOutputBuffer> availableOutputBuffers; private final ArrayDeque<SubtitleOutputBuffer> availableOutputBuffers;
private final PriorityQueue<CeaInputBuffer> queuedInputBuffers; private final PriorityQueue<CeaInputBuffer> queuedInputBuffers;
private CeaInputBuffer dequeuedInputBuffer; private CeaInputBuffer dequeuedInputBuffer;
@ -44,11 +44,11 @@ import java.util.PriorityQueue;
private long queuedInputBufferCount; private long queuedInputBufferCount;
public CeaDecoder() { public CeaDecoder() {
availableInputBuffers = new LinkedList<>(); availableInputBuffers = new ArrayDeque<>();
for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { for (int i = 0; i < NUM_INPUT_BUFFERS; i++) {
availableInputBuffers.add(new CeaInputBuffer()); availableInputBuffers.add(new CeaInputBuffer());
} }
availableOutputBuffers = new LinkedList<>(); availableOutputBuffers = new ArrayDeque<>();
for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) {
availableOutputBuffers.add(new CeaOutputBuffer()); availableOutputBuffers.add(new CeaOutputBuffer());
} }

View File

@ -62,7 +62,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
super("SsaDecoder"); super("SsaDecoder");
if (initializationData != null && !initializationData.isEmpty()) { if (initializationData != null && !initializationData.isEmpty()) {
haveInitializationData = true; haveInitializationData = true;
String formatLine = new String(initializationData.get(0)); String formatLine = Util.fromUtf8Bytes(initializationData.get(0));
Assertions.checkArgument(formatLine.startsWith(FORMAT_LINE_PREFIX)); Assertions.checkArgument(formatLine.startsWith(FORMAT_LINE_PREFIX));
parseFormatLine(formatLine); parseFormatLine(formatLine);
parseHeader(new ParsableByteArray(initializationData.get(1))); parseHeader(new ParsableByteArray(initializationData.get(1)));

View File

@ -26,8 +26,8 @@ import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.util.XmlPullParserUtil; import com.google.android.exoplayer2.util.XmlPullParserUtil;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayDeque;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -109,13 +109,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length);
xmlParser.setInput(inputStream, null); xmlParser.setInput(inputStream, null);
TtmlSubtitle ttmlSubtitle = null; TtmlSubtitle ttmlSubtitle = null;
LinkedList<TtmlNode> nodeStack = new LinkedList<>(); ArrayDeque<TtmlNode> nodeStack = new ArrayDeque<>();
int unsupportedNodeDepth = 0; int unsupportedNodeDepth = 0;
int eventType = xmlParser.getEventType(); int eventType = xmlParser.getEventType();
FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE; FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE;
CellResolution cellResolution = DEFAULT_CELL_RESOLUTION; CellResolution cellResolution = DEFAULT_CELL_RESOLUTION;
while (eventType != XmlPullParser.END_DOCUMENT) { while (eventType != XmlPullParser.END_DOCUMENT) {
TtmlNode parent = nodeStack.peekLast(); TtmlNode parent = nodeStack.peek();
if (unsupportedNodeDepth == 0) { if (unsupportedNodeDepth == 0) {
String name = xmlParser.getName(); String name = xmlParser.getName();
if (eventType == XmlPullParser.START_TAG) { if (eventType == XmlPullParser.START_TAG) {
@ -131,7 +131,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} else { } else {
try { try {
TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate); TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate);
nodeStack.addLast(node); nodeStack.push(node);
if (parent != null) { if (parent != null) {
parent.addChild(node); parent.addChild(node);
} }
@ -145,9 +145,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
parent.addChild(TtmlNode.buildTextNode(xmlParser.getText())); parent.addChild(TtmlNode.buildTextNode(xmlParser.getText()));
} else if (eventType == XmlPullParser.END_TAG) { } else if (eventType == XmlPullParser.END_TAG) {
if (xmlParser.getName().equals(TtmlNode.TAG_TT)) { if (xmlParser.getName().equals(TtmlNode.TAG_TT)) {
ttmlSubtitle = new TtmlSubtitle(nodeStack.getLast(), globalStyles, regionMap); ttmlSubtitle = new TtmlSubtitle(nodeStack.peek(), globalStyles, regionMap);
} }
nodeStack.removeLast(); nodeStack.pop();
} }
} else { } else {
if (eventType == XmlPullParser.START_TAG) { if (eventType == XmlPullParser.START_TAG) {
@ -178,7 +178,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
float frameRateMultiplier = 1; float frameRateMultiplier = 1;
String frameRateMultiplierString = xmlParser.getAttributeValue(TTP, "frameRateMultiplier"); String frameRateMultiplierString = xmlParser.getAttributeValue(TTP, "frameRateMultiplier");
if (frameRateMultiplierString != null) { if (frameRateMultiplierString != null) {
String[] parts = frameRateMultiplierString.split(" "); String[] parts = Util.split(frameRateMultiplierString, " ");
if (parts.length != 2) { if (parts.length != 2) {
throw new SubtitleDecoderException("frameRateMultiplier doesn't have 2 parts"); throw new SubtitleDecoderException("frameRateMultiplier doesn't have 2 parts");
} }
@ -354,7 +354,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} }
private String[] parseStyleIds(String parentStyleIds) { private String[] parseStyleIds(String parentStyleIds) {
return parentStyleIds.split("\\s+"); parentStyleIds = parentStyleIds.trim();
return parentStyleIds.isEmpty() ? new String[0] : Util.split(parentStyleIds, "\\s+");
} }
private TtmlStyle parseStyleAttributes(XmlPullParser parser, TtmlStyle style) { private TtmlStyle parseStyleAttributes(XmlPullParser parser, TtmlStyle style) {
@ -531,7 +532,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
private static void parseFontSize(String expression, TtmlStyle out) throws private static void parseFontSize(String expression, TtmlStyle out) throws
SubtitleDecoderException { SubtitleDecoderException {
String[] expressions = expression.split("\\s+"); String[] expressions = Util.split(expression, "\\s+");
Matcher matcher; Matcher matcher;
if (expressions.length == 1) { if (expressions.length == 1) {
matcher = FONT_SIZE.matcher(expression); matcher = FONT_SIZE.matcher(expression);

View File

@ -92,7 +92,8 @@ public final class Tx3gDecoder extends SimpleSubtitleDecoder {
| ((initializationBytes[27] & 0xFF) << 16) | ((initializationBytes[27] & 0xFF) << 16)
| ((initializationBytes[28] & 0xFF) << 8) | ((initializationBytes[28] & 0xFF) << 8)
| (initializationBytes[29] & 0xFF); | (initializationBytes[29] & 0xFF);
String fontFamily = new String(initializationBytes, 43, initializationBytes.length - 43); String fontFamily =
Util.fromUtf8Bytes(initializationBytes, 43, initializationBytes.length - 43);
defaultFontFamily = TX3G_SERIF.equals(fontFamily) ? C.SERIF_NAME : C.SANS_SERIF_NAME; defaultFontFamily = TX3G_SERIF.equals(fontFamily) ? C.SERIF_NAME : C.SANS_SERIF_NAME;
//font size (initializationBytes[25]) is 5% of video height //font size (initializationBytes[25]) is 5% of video height
calculatedVideoTrackHeight = 20 * initializationBytes[25]; calculatedVideoTrackHeight = 20 * initializationBytes[25];

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.webvtt;
import android.text.TextUtils; import android.text.TextUtils;
import com.google.android.exoplayer2.util.ColorParser; import com.google.android.exoplayer2.util.ColorParser;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -314,7 +315,7 @@ import java.util.regex.Pattern;
} }
selector = selector.substring(0, voiceStartIndex); selector = selector.substring(0, voiceStartIndex);
} }
String[] classDivision = selector.split("\\."); String[] classDivision = Util.split(selector, "\\.");
String tagAndIdDivision = classDivision[0]; String tagAndIdDivision = classDivision[0];
int idPrefixIndex = tagAndIdDivision.indexOf('#'); int idPrefixIndex = tagAndIdDivision.indexOf('#');
if (idPrefixIndex != -1) { if (idPrefixIndex != -1) {

View File

@ -78,7 +78,8 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
int boxType = sampleData.readInt(); int boxType = sampleData.readInt();
remainingCueBoxBytes -= BOX_HEADER_SIZE; remainingCueBoxBytes -= BOX_HEADER_SIZE;
int payloadLength = boxSize - BOX_HEADER_SIZE; int payloadLength = boxSize - BOX_HEADER_SIZE;
String boxPayload = new String(sampleData.data, sampleData.getPosition(), payloadLength); String boxPayload =
Util.fromUtf8Bytes(sampleData.data, sampleData.getPosition(), payloadLength);
sampleData.skipBytes(payloadLength); sampleData.skipBytes(payloadLength);
remainingCueBoxBytes -= payloadLength; remainingCueBoxBytes -= payloadLength;
if (boxType == TYPE_sttg) { if (boxType == TYPE_sttg) {

View File

@ -34,11 +34,12 @@ import android.text.style.UnderlineSpan;
import android.util.Log; import android.util.Log;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Stack;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -157,7 +158,7 @@ public final class WebvttCueParser {
/* package */ static void parseCueText(String id, String markup, WebvttCue.Builder builder, /* package */ static void parseCueText(String id, String markup, WebvttCue.Builder builder,
List<WebvttCssStyle> styles) { List<WebvttCssStyle> styles) {
SpannableStringBuilder spannedText = new SpannableStringBuilder(); SpannableStringBuilder spannedText = new SpannableStringBuilder();
Stack<StartTag> startTagStack = new Stack<>(); ArrayDeque<StartTag> startTagStack = new ArrayDeque<>();
List<StyleMatch> scratchStyleMatches = new ArrayList<>(); List<StyleMatch> scratchStyleMatches = new ArrayList<>();
int pos = 0; int pos = 0;
while (pos < markup.length()) { while (pos < markup.length()) {
@ -456,7 +457,7 @@ public final class WebvttCueParser {
if (tagExpression.isEmpty()) { if (tagExpression.isEmpty()) {
return null; return null;
} }
return tagExpression.split("[ \\.]")[0]; return Util.splitAtFirst(tagExpression, "[ \\.]")[0];
} }
private static void getApplicableStyles(List<WebvttCssStyle> declaredStyles, String id, private static void getApplicableStyles(List<WebvttCssStyle> declaredStyles, String id,
@ -518,7 +519,7 @@ public final class WebvttCueParser {
voice = fullTagExpression.substring(voiceStartIndex).trim(); voice = fullTagExpression.substring(voiceStartIndex).trim();
fullTagExpression = fullTagExpression.substring(0, voiceStartIndex); fullTagExpression = fullTagExpression.substring(0, voiceStartIndex);
} }
String[] nameAndClasses = fullTagExpression.split("\\."); String[] nameAndClasses = Util.split(fullTagExpression, "\\.");
String name = nameAndClasses[0]; String name = nameAndClasses[0];
String[] classes; String[] classes;
if (nameAndClasses.length > 1) { if (nameAndClasses.length > 1) {

View File

@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text.webvtt;
import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -53,8 +54,8 @@ public final class WebvttParserUtil {
*/ */
public static long parseTimestampUs(String timestamp) throws NumberFormatException { public static long parseTimestampUs(String timestamp) throws NumberFormatException {
long value = 0; long value = 0;
String[] parts = timestamp.split("\\.", 2); String[] parts = Util.splitAtFirst(timestamp, "\\.");
String[] subparts = parts[0].split(":"); String[] subparts = Util.split(parts[0], ":");
for (String subpart : subparts) { for (String subpart : subparts) {
value = (value * 60) + Long.parseLong(subpart); value = (value * 60) + Long.parseLong(subpart);
} }

View File

@ -110,6 +110,7 @@ public abstract class BaseTrackSelection implements TrackSelection {
} }
@Override @Override
@SuppressWarnings("ReferenceEquality")
public final int indexOf(Format format) { public final int indexOf(Format format) {
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
if (formats[i] == format) { if (formats[i] == format) {
@ -183,7 +184,9 @@ public abstract class BaseTrackSelection implements TrackSelection {
return hashCode; return hashCode;
} }
// Track groups are compared by identity not value, as distinct groups may have the same value.
@Override @Override
@SuppressWarnings("ReferenceEquality")
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) { if (this == obj) {
return true; return true;

View File

@ -91,7 +91,9 @@ public interface TrackSelection {
int getIndexInTrackGroup(int index); int getIndexInTrackGroup(int index);
/** /**
* Returns the index in the selection of the track with the specified format. * Returns the index in the selection of the track with the specified format. The format is
* located by identity so, for example, {@code selection.indexOf(selection.getFormat(index)) ==
* index} even if multiple selected tracks have formats that contain the same values.
* *
* @param format The format. * @param format The format.
* @return The index in the selection, or {@link C#INDEX_UNSET} if the track with the specified * @return The index in the selection, or {@link C#INDEX_UNSET} if the track with the specified

View File

@ -19,6 +19,7 @@ import android.net.Uri;
import android.util.Base64; import android.util.Base64;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.net.URLDecoder; import java.net.URLDecoder;
@ -41,8 +42,8 @@ public final class DataSchemeDataSource implements DataSource {
if (!SCHEME_DATA.equals(scheme)) { if (!SCHEME_DATA.equals(scheme)) {
throw new ParserException("Unsupported scheme: " + scheme); throw new ParserException("Unsupported scheme: " + scheme);
} }
String[] uriParts = uri.getSchemeSpecificPart().split(","); String[] uriParts = Util.split(uri.getSchemeSpecificPart(), ",");
if (uriParts.length > 2) { if (uriParts.length != 2) {
throw new ParserException("Unexpected URI format: " + uri); throw new ParserException("Unexpected URI format: " + uri);
} }
String dataString = uriParts[1]; String dataString = uriParts[1];

View File

@ -20,7 +20,7 @@ import android.support.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
/** Helper classes to easily access and modify internal metadata values. */ /** Helper classes to easily access and modify internal metadata values. */
/*package*/ final class ContentMetadataInternal { /* package */ final class ContentMetadataInternal {
private static final String PREFIX = ContentMetadata.INTERNAL_METADATA_NAME_PREFIX; private static final String PREFIX = ContentMetadata.INTERNAL_METADATA_NAME_PREFIX;
private static final String METADATA_NAME_REDIRECTED_URI = PREFIX + "redir"; private static final String METADATA_NAME_REDIRECTED_URI = PREFIX + "redir";
@ -59,4 +59,8 @@ import com.google.android.exoplayer2.C;
public static void removeRedirectedUri(ContentMetadataMutations mutations) { public static void removeRedirectedUri(ContentMetadataMutations mutations) {
mutations.remove(METADATA_NAME_REDIRECTED_URI); mutations.remove(METADATA_NAME_REDIRECTED_URI);
} }
private ContentMetadataInternal() {
// Prevent instantiation.
}
} }

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.upstream.crypto; package com.google.android.exoplayer2.upstream.crypto;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
@ -49,7 +50,9 @@ public final class AesFlushingCipher {
flushedBlock = new byte[blockSize]; flushedBlock = new byte[blockSize];
long counter = offset / blockSize; long counter = offset / blockSize;
int startPadding = (int) (offset % blockSize); int startPadding = (int) (offset % blockSize);
cipher.init(mode, new SecretKeySpec(secretKey, cipher.getAlgorithm().split("/")[0]), cipher.init(
mode,
new SecretKeySpec(secretKey, Util.splitAtFirst(cipher.getAlgorithm(), "/")[0]),
new IvParameterSpec(getInitializationVector(nonce, counter))); new IvParameterSpec(getInitializationVector(nonce, counter)));
if (startPadding != 0) { if (startPadding != 0) {
updateInPlace(new byte[startPadding], 0, startPadding); updateInPlace(new byte[startPadding], 0, startPadding);

View File

@ -26,7 +26,7 @@ import java.util.regex.Pattern;
* *
* @see <a href="https://w3c.github.io/webvtt/#styling">WebVTT CSS Styling</a> * @see <a href="https://w3c.github.io/webvtt/#styling">WebVTT CSS Styling</a>
* @see <a href="https://www.w3.org/TR/ttml2/">Timed Text Markup Language 2 (TTML2) - 10.3.5</a> * @see <a href="https://www.w3.org/TR/ttml2/">Timed Text Markup Language 2 (TTML2) - 10.3.5</a>
**/ */
public final class ColorParser { public final class ColorParser {
private static final String RGB = "rgb"; private static final String RGB = "rgb";
@ -271,4 +271,7 @@ public final class ColorParser {
COLOR_MAP.put("yellowgreen", 0xFF9ACD32); COLOR_MAP.put("yellowgreen", 0xFF9ACD32);
} }
private ColorParser() {
// Prevent instantiation.
}
} }

View File

@ -144,7 +144,7 @@ public final class MimeTypes {
if (codecs == null) { if (codecs == null) {
return null; return null;
} }
String[] codecList = codecs.split(","); String[] codecList = Util.split(codecs, ",");
for (String codec : codecList) { for (String codec : codecList) {
String mimeType = getMediaMimeType(codec); String mimeType = getMediaMimeType(codec);
if (mimeType != null && isVideo(mimeType)) { if (mimeType != null && isVideo(mimeType)) {
@ -164,7 +164,7 @@ public final class MimeTypes {
if (codecs == null) { if (codecs == null) {
return null; return null;
} }
String[] codecList = codecs.split(","); String[] codecList = Util.split(codecs, ",");
for (String codec : codecList) { for (String codec : codecList) {
String mimeType = getMediaMimeType(codec); String mimeType = getMediaMimeType(codec);
if (mimeType != null && isAudio(mimeType)) { if (mimeType != null && isAudio(mimeType)) {

View File

@ -175,7 +175,7 @@ public final class ParsableBitArray {
bitOffset -= 8; bitOffset -= 8;
returnValue |= (data[byteOffset++] & 0xFF) << bitOffset; returnValue |= (data[byteOffset++] & 0xFF) << bitOffset;
} }
returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; returnValue |= (data[byteOffset] & 0xFF) >> (8 - bitOffset);
returnValue &= 0xFFFFFFFF >>> (32 - numBits); returnValue &= 0xFFFFFFFF >>> (32 - numBits);
if (bitOffset == 8) { if (bitOffset == 8) {
bitOffset = 0; bitOffset = 0;
@ -199,17 +199,18 @@ public final class ParsableBitArray {
int to = offset + (numBits >> 3) /* numBits / 8 */; int to = offset + (numBits >> 3) /* numBits / 8 */;
for (int i = offset; i < to; i++) { for (int i = offset; i < to; i++) {
buffer[i] = (byte) (data[byteOffset++] << bitOffset); buffer[i] = (byte) (data[byteOffset++] << bitOffset);
buffer[i] |= (data[byteOffset] & 0xFF) >> (8 - bitOffset); buffer[i] = (byte) (buffer[i] | ((data[byteOffset] & 0xFF) >> (8 - bitOffset)));
} }
// Trailing bits. // Trailing bits.
int bitsLeft = numBits & 7 /* numBits % 8 */; int bitsLeft = numBits & 7 /* numBits % 8 */;
if (bitsLeft == 0) { if (bitsLeft == 0) {
return; return;
} }
buffer[to] &= 0xFF >> bitsLeft; // Set to 0 the bits that are going to be overwritten. // Set bits that are going to be overwritten to 0.
buffer[to] = (byte) (buffer[to] & (0xFF >> bitsLeft));
if (bitOffset + bitsLeft > 8) { if (bitOffset + bitsLeft > 8) {
// We read the rest of data[byteOffset] and increase byteOffset. // We read the rest of data[byteOffset] and increase byteOffset.
buffer[to] |= (byte) ((data[byteOffset++] & 0xFF) << bitOffset); buffer[to] = (byte) (buffer[to] | ((data[byteOffset++] & 0xFF) << bitOffset));
bitOffset -= 8; bitOffset -= 8;
} }
bitOffset += bitsLeft; bitOffset += bitsLeft;
@ -280,9 +281,10 @@ public final class ParsableBitArray {
int firstByteReadSize = Math.min(8 - bitOffset, numBits); int firstByteReadSize = Math.min(8 - bitOffset, numBits);
int firstByteRightPaddingSize = 8 - bitOffset - firstByteReadSize; int firstByteRightPaddingSize = 8 - bitOffset - firstByteReadSize;
int firstByteBitmask = (0xFF00 >> bitOffset) | ((1 << firstByteRightPaddingSize) - 1); int firstByteBitmask = (0xFF00 >> bitOffset) | ((1 << firstByteRightPaddingSize) - 1);
data[byteOffset] &= firstByteBitmask; data[byteOffset] = (byte) (data[byteOffset] & firstByteBitmask);
int firstByteInputBits = value >>> (numBits - firstByteReadSize); int firstByteInputBits = value >>> (numBits - firstByteReadSize);
data[byteOffset] |= firstByteInputBits << firstByteRightPaddingSize; data[byteOffset] =
(byte) (data[byteOffset] | (firstByteInputBits << firstByteRightPaddingSize));
remainingBitsToRead -= firstByteReadSize; remainingBitsToRead -= firstByteReadSize;
int currentByteIndex = byteOffset + 1; int currentByteIndex = byteOffset + 1;
while (remainingBitsToRead > 8) { while (remainingBitsToRead > 8) {
@ -290,9 +292,11 @@ public final class ParsableBitArray {
remainingBitsToRead -= 8; remainingBitsToRead -= 8;
} }
int lastByteRightPaddingSize = 8 - remainingBitsToRead; int lastByteRightPaddingSize = 8 - remainingBitsToRead;
data[currentByteIndex] &= (1 << lastByteRightPaddingSize) - 1; data[currentByteIndex] =
(byte) (data[currentByteIndex] & ((1 << lastByteRightPaddingSize) - 1));
int lastByteInput = value & ((1 << remainingBitsToRead) - 1); int lastByteInput = value & ((1 << remainingBitsToRead) - 1);
data[currentByteIndex] |= lastByteInput << lastByteRightPaddingSize; data[currentByteIndex] =
(byte) (data[currentByteIndex] | (lastByteInput << lastByteRightPaddingSize));
skipBits(numBits); skipBits(numBits);
assertValidOffset(); assertValidOffset();
} }

View File

@ -470,7 +470,7 @@ public final class ParsableByteArray {
if (lastIndex < limit && data[lastIndex] == 0) { if (lastIndex < limit && data[lastIndex] == 0) {
stringLength--; stringLength--;
} }
String result = new String(data, position, stringLength); String result = Util.fromUtf8Bytes(data, position, stringLength);
position += length; position += length;
return result; return result;
} }
@ -489,7 +489,7 @@ public final class ParsableByteArray {
while (stringLimit < limit && data[stringLimit] != 0) { while (stringLimit < limit && data[stringLimit] != 0) {
stringLimit++; stringLimit++;
} }
String string = new String(data, position, stringLimit - position); String string = Util.fromUtf8Bytes(data, position, stringLimit - position);
position = stringLimit; position = stringLimit;
if (position < limit) { if (position < limit) {
position++; position++;
@ -520,7 +520,7 @@ public final class ParsableByteArray {
// There's a byte order mark at the start of the line. Discard it. // There's a byte order mark at the start of the line. Discard it.
position += 3; position += 3;
} }
String line = new String(data, position, lineLimit - position); String line = Util.fromUtf8Bytes(data, position, lineLimit - position);
position = lineLimit; position = lineLimit;
if (position == limit) { if (position == limit) {
return line; return line;

View File

@ -140,7 +140,7 @@ public final class ParsableNalUnitBitArray {
returnValue |= (data[byteOffset] & 0xFF) << bitOffset; returnValue |= (data[byteOffset] & 0xFF) << bitOffset;
byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1; byteOffset += shouldSkipByte(byteOffset + 1) ? 2 : 1;
} }
returnValue |= (data[byteOffset] & 0xFF) >> 8 - bitOffset; returnValue |= (data[byteOffset] & 0xFF) >> (8 - bitOffset);
returnValue &= 0xFFFFFFFF >>> (32 - numBits); returnValue &= 0xFFFFFFFF >>> (32 - numBits);
if (bitOffset == 8) { if (bitOffset == 8) {
bitOffset = 0; bitOffset = 0;

View File

@ -332,6 +332,18 @@ public final class Util {
return new String(bytes, Charset.forName(C.UTF8_NAME)); return new String(bytes, Charset.forName(C.UTF8_NAME));
} }
/**
* Returns a new {@link String} constructed by decoding UTF-8 encoded bytes in a subarray.
*
* @param bytes The UTF-8 encoded bytes to decode.
* @param offset The index of the first byte to decode.
* @param length The number of bytes to decode.
* @return The string.
*/
public static String fromUtf8Bytes(byte[] bytes, int offset, int length) {
return new String(bytes, offset, length, Charset.forName(C.UTF8_NAME));
}
/** /**
* Returns a new byte array containing the code points of a {@link String} encoded using UTF-8. * Returns a new byte array containing the code points of a {@link String} encoded using UTF-8.
* *
@ -342,6 +354,33 @@ public final class Util {
return value.getBytes(Charset.forName(C.UTF8_NAME)); return value.getBytes(Charset.forName(C.UTF8_NAME));
} }
/**
* Splits a string using {@code value.split(regex, -1}). Note: this is is similar to {@link
* String#split(String)} but empty matches at the end of the string will not be omitted from the
* returned array.
*
* @param value The string to split.
* @param regex A delimiting regular expression.
* @return The array of strings resulting from splitting the string.
*/
public static String[] split(String value, String regex) {
return value.split(regex, /* limit= */ -1);
}
/**
* Splits the string at the first occurrence of the delimiter {@code regex}. If the delimiter does
* not match, returns an array with one element which is the input string. If the delimiter does
* match, returns an array with the portion of the string before the delimiter and the rest of the
* string.
*
* @param value The string.
* @param regex A delimiting regular expression.
* @return The string split by the first occurrence of the delimiter.
*/
public static String[] splitAtFirst(String value, String regex) {
return value.split(regex, /* limit= */ 2);
}
/** /**
* Returns whether the given character is a carriage return ('\r') or a line feed ('\n'). * Returns whether the given character is a carriage return ('\r') or a line feed ('\n').
* *
@ -978,7 +1017,7 @@ public final class Util {
if (TextUtils.isEmpty(codecs)) { if (TextUtils.isEmpty(codecs)) {
return null; return null;
} }
String[] codecArray = codecs.trim().split("(\\s*,\\s*)"); String[] codecArray = split(codecs.trim(), "(\\s*,\\s*)");
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (String codec : codecArray) { for (String codec : codecArray) {
if (trackType == MimeTypes.getTrackTypeOfCodec(codec)) { if (trackType == MimeTypes.getTrackTypeOfCodec(codec)) {
@ -1454,7 +1493,7 @@ public final class Util {
// If we managed to read sys.display-size, attempt to parse it. // If we managed to read sys.display-size, attempt to parse it.
if (!TextUtils.isEmpty(sysDisplaySize)) { if (!TextUtils.isEmpty(sysDisplaySize)) {
try { try {
String[] sysDisplaySizeParts = sysDisplaySize.trim().split("x"); String[] sysDisplaySizeParts = split(sysDisplaySize.trim(), "x");
if (sysDisplaySizeParts.length == 2) { if (sysDisplaySizeParts.length == 2) {
int width = Integer.parseInt(sysDisplaySizeParts[0]); int width = Integer.parseInt(sysDisplaySizeParts[0]);
int height = Integer.parseInt(sysDisplaySizeParts[1]); int height = Integer.parseInt(sysDisplaySizeParts[1]);