Remove DownloadAction serialization
Implement Parcelable instead, which is a more intented way of passing structured data via Intents. PiperOrigin-RevId: 242646441
This commit is contained in:
parent
14d40b0faf
commit
d3b63a97ad
@ -136,7 +136,8 @@ public class DemoApplication extends Application {
|
|||||||
ActionFile actionFile = new ActionFile(new File(getDownloadDirectory(), file));
|
ActionFile actionFile = new ActionFile(new File(getDownloadDirectory(), file));
|
||||||
if (actionFile.exists()) {
|
if (actionFile.exists()) {
|
||||||
try {
|
try {
|
||||||
DownloadIndexUtil.upgradeActionFile(actionFile, downloadIndex, null);
|
DownloadIndexUtil.mergeActionFile(
|
||||||
|
actionFile, /* downloadIdProvider= */ null, downloadIndex);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Upgrading action file failed", e);
|
Log.e(TAG, "Upgrading action file failed", e);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException;
|
import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException;
|
||||||
import com.google.android.exoplayer2.util.AtomicFile;
|
import com.google.android.exoplayer2.util.AtomicFile;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -23,10 +25,9 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/** Loads {@link DownloadAction DownloadActions} from legacy action files. */
|
||||||
* Stores and loads {@link DownloadAction}s to/from a file.
|
|
||||||
*/
|
|
||||||
public final class ActionFile {
|
public final class ActionFile {
|
||||||
|
|
||||||
private static final int VERSION = 0;
|
private static final int VERSION = 0;
|
||||||
@ -34,17 +35,28 @@ public final class ActionFile {
|
|||||||
private final AtomicFile atomicFile;
|
private final AtomicFile atomicFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param actionFile File to be used to store and load {@link DownloadAction}s.
|
* @param actionFile The file from which {@link DownloadAction DownloadActions} will be loaded.
|
||||||
*/
|
*/
|
||||||
public ActionFile(File actionFile) {
|
public ActionFile(File actionFile) {
|
||||||
atomicFile = new AtomicFile(actionFile);
|
atomicFile = new AtomicFile(actionFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether the file or its backup exists. */
|
||||||
|
public boolean exists() {
|
||||||
|
return atomicFile.exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Deletes the action file and its backup. */
|
||||||
|
public void delete() {
|
||||||
|
atomicFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads {@link DownloadAction}s from file.
|
* Loads {@link DownloadAction DownloadActions} from the file.
|
||||||
*
|
*
|
||||||
* @return Loaded DownloadActions. If the action file doesn't exists returns an empty array.
|
* @return The loaded {@link DownloadAction DownloadActions}, or an empty array if the file does
|
||||||
* @throws IOException If there is an error during loading.
|
* not exist.
|
||||||
|
* @throws IOException If there is an error reading the file.
|
||||||
*/
|
*/
|
||||||
public DownloadAction[] load() throws IOException {
|
public DownloadAction[] load() throws IOException {
|
||||||
if (!exists()) {
|
if (!exists()) {
|
||||||
@ -62,7 +74,7 @@ public final class ActionFile {
|
|||||||
ArrayList<DownloadAction> actions = new ArrayList<>();
|
ArrayList<DownloadAction> actions = new ArrayList<>();
|
||||||
for (int i = 0; i < actionCount; i++) {
|
for (int i = 0; i < actionCount; i++) {
|
||||||
try {
|
try {
|
||||||
actions.add(DownloadAction.deserializeFromStream(dataInputStream));
|
actions.add(readDownloadAction(dataInputStream));
|
||||||
} catch (UnsupportedActionException e) {
|
} catch (UnsupportedActionException e) {
|
||||||
// remove DownloadAction is not supported. Ignore the exception and continue loading rest.
|
// remove DownloadAction is not supported. Ignore the exception and continue loading rest.
|
||||||
}
|
}
|
||||||
@ -73,13 +85,74 @@ public final class ActionFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns whether the file or its backup exists. */
|
private static DownloadAction readDownloadAction(DataInputStream input) throws IOException {
|
||||||
public boolean exists() {
|
String type = input.readUTF();
|
||||||
return atomicFile.exists();
|
int version = input.readInt();
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(input.readUTF());
|
||||||
|
boolean isRemoveAction = input.readBoolean();
|
||||||
|
|
||||||
|
int dataLength = input.readInt();
|
||||||
|
byte[] data;
|
||||||
|
if (dataLength != 0) {
|
||||||
|
data = new byte[dataLength];
|
||||||
|
input.readFully(data);
|
||||||
|
} else {
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized version 0 progressive actions did not contain keys.
|
||||||
|
boolean isLegacyProgressive = version == 0 && DownloadAction.TYPE_PROGRESSIVE.equals(type);
|
||||||
|
List<StreamKey> keys = new ArrayList<>();
|
||||||
|
if (!isLegacyProgressive) {
|
||||||
|
int keyCount = input.readInt();
|
||||||
|
for (int i = 0; i < keyCount; i++) {
|
||||||
|
keys.add(readKey(type, version, input));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key.
|
||||||
|
boolean isLegacySegmented =
|
||||||
|
version < 2
|
||||||
|
&& (DownloadAction.TYPE_DASH.equals(type)
|
||||||
|
|| DownloadAction.TYPE_HLS.equals(type)
|
||||||
|
|| DownloadAction.TYPE_SS.equals(type));
|
||||||
|
String customCacheKey = null;
|
||||||
|
if (!isLegacySegmented) {
|
||||||
|
customCacheKey = input.readBoolean() ? input.readUTF() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialized version 0, 1 and 2 did not contain an id. We need to generate one.
|
||||||
|
String id = version < 3 ? generateDownloadActionId(uri, customCacheKey) : input.readUTF();
|
||||||
|
|
||||||
|
if (isRemoveAction) {
|
||||||
|
// Remove actions are not supported anymore.
|
||||||
|
throw new UnsupportedActionException();
|
||||||
|
}
|
||||||
|
return new DownloadAction(id, type, uri, keys, customCacheKey, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Delete the action file and its backup. */
|
private static StreamKey readKey(String type, int version, DataInputStream input)
|
||||||
public void delete() {
|
throws IOException {
|
||||||
atomicFile.delete();
|
int periodIndex;
|
||||||
|
int groupIndex;
|
||||||
|
int trackIndex;
|
||||||
|
|
||||||
|
// Serialized version 0 HLS/SS actions did not contain a period index.
|
||||||
|
if ((DownloadAction.TYPE_HLS.equals(type) || DownloadAction.TYPE_SS.equals(type))
|
||||||
|
&& version == 0) {
|
||||||
|
periodIndex = 0;
|
||||||
|
groupIndex = input.readInt();
|
||||||
|
trackIndex = input.readInt();
|
||||||
|
} else {
|
||||||
|
periodIndex = input.readInt();
|
||||||
|
groupIndex = input.readInt();
|
||||||
|
trackIndex = input.readInt();
|
||||||
|
}
|
||||||
|
return new StreamKey(periodIndex, groupIndex, trackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String generateDownloadActionId(Uri uri, @Nullable String customCacheKey) {
|
||||||
|
return customCacheKey != null ? customCacheKey : uri.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ public final class DefaultDownloadIndex implements DownloadIndex {
|
|||||||
|
|
||||||
private static DownloadState getDownloadStateForCurrentRow(Cursor cursor) {
|
private static DownloadState getDownloadStateForCurrentRow(Cursor cursor) {
|
||||||
DownloadAction action =
|
DownloadAction action =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
cursor.getString(COLUMN_INDEX_ID),
|
cursor.getString(COLUMN_INDEX_ID),
|
||||||
cursor.getString(COLUMN_INDEX_TYPE),
|
cursor.getString(COLUMN_INDEX_TYPE),
|
||||||
Uri.parse(cursor.getString(COLUMN_INDEX_URI)),
|
Uri.parse(cursor.getString(COLUMN_INDEX_URI)),
|
||||||
|
@ -15,24 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
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;
|
||||||
|
|
||||||
/** Contains the necessary parameters for a download action. */
|
/** Contains the necessary parameters for a download action. */
|
||||||
public final class DownloadAction {
|
public final class DownloadAction implements Parcelable {
|
||||||
|
|
||||||
/** Thrown when the encoded action data belongs to an unsupported DownloadAction type. */
|
/** Thrown when the encoded action data belongs to an unsupported DownloadAction type. */
|
||||||
public static class UnsupportedActionException extends IOException {}
|
public static class UnsupportedActionException extends IOException {}
|
||||||
@ -46,56 +44,6 @@ public final class DownloadAction {
|
|||||||
/** Type for SmoothStreaming downloads. */
|
/** Type for SmoothStreaming downloads. */
|
||||||
public static final String TYPE_SS = "ss";
|
public static final String TYPE_SS = "ss";
|
||||||
|
|
||||||
private static final int VERSION = 3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserializes a download action from the {@code data}.
|
|
||||||
*
|
|
||||||
* @param data The action data to deserialize.
|
|
||||||
* @return The deserialized action.
|
|
||||||
* @throws IOException If the data could not be deserialized.
|
|
||||||
* @throws UnsupportedActionException If the data belongs to an unsupported {@link DownloadAction}
|
|
||||||
* type. Input read position is set to the end of the data.
|
|
||||||
*/
|
|
||||||
public static DownloadAction fromByteArray(byte[] data) throws IOException {
|
|
||||||
ByteArrayInputStream input = new ByteArrayInputStream(data);
|
|
||||||
return deserializeFromStream(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserializes a single download action from {@code input}.
|
|
||||||
*
|
|
||||||
* @param input The stream from which to read.
|
|
||||||
* @return The deserialized action.
|
|
||||||
* @throws IOException If there is an IO error reading from {@code input}, or if the data could
|
|
||||||
* not be deserialized.
|
|
||||||
* @throws UnsupportedActionException If the data belongs to an unsupported {@link DownloadAction}
|
|
||||||
* type. Input read position is set to the end of the data.
|
|
||||||
*/
|
|
||||||
public static DownloadAction deserializeFromStream(InputStream input) throws IOException {
|
|
||||||
return readFromStream(new DataInputStream(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DASH download action.
|
|
||||||
*
|
|
||||||
* @param id The content id.
|
|
||||||
* @param type The type of the action.
|
|
||||||
* @param uri The URI of the media to be downloaded.
|
|
||||||
* @param keys Keys of streams to be downloaded. If empty, all streams will be downloaded.
|
|
||||||
* @param customCacheKey A custom key for cache indexing, or null.
|
|
||||||
* @param data Optional custom data for this action. If {@code null} an empty array will be used.
|
|
||||||
*/
|
|
||||||
public static DownloadAction createDownloadAction(
|
|
||||||
String id,
|
|
||||||
String type,
|
|
||||||
Uri uri,
|
|
||||||
List<StreamKey> keys,
|
|
||||||
@Nullable String customCacheKey,
|
|
||||||
@Nullable byte[] data) {
|
|
||||||
return new DownloadAction(id, type, uri, keys, customCacheKey, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The unique content id. */
|
/** The unique content id. */
|
||||||
public final String id;
|
public final String id;
|
||||||
/** The type of the action. */
|
/** The type of the action. */
|
||||||
@ -117,7 +65,7 @@ public final class DownloadAction {
|
|||||||
* @param customCacheKey See {@link #customCacheKey}.
|
* @param customCacheKey See {@link #customCacheKey}.
|
||||||
* @param data See {@link #data}.
|
* @param data See {@link #data}.
|
||||||
*/
|
*/
|
||||||
private DownloadAction(
|
public DownloadAction(
|
||||||
String id,
|
String id,
|
||||||
String type,
|
String type,
|
||||||
Uri uri,
|
Uri uri,
|
||||||
@ -127,13 +75,38 @@ public final class DownloadAction {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.customCacheKey = customCacheKey;
|
|
||||||
ArrayList<StreamKey> mutableKeys = new ArrayList<>(streamKeys);
|
ArrayList<StreamKey> mutableKeys = new ArrayList<>(streamKeys);
|
||||||
Collections.sort(mutableKeys);
|
Collections.sort(mutableKeys);
|
||||||
this.streamKeys = Collections.unmodifiableList(mutableKeys);
|
this.streamKeys = Collections.unmodifiableList(mutableKeys);
|
||||||
|
this.customCacheKey = customCacheKey;
|
||||||
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
|
this.data = data != null ? Arrays.copyOf(data, data.length) : Util.EMPTY_BYTE_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package */ DownloadAction(Parcel in) {
|
||||||
|
id = castNonNull(in.readString());
|
||||||
|
type = castNonNull(in.readString());
|
||||||
|
uri = Uri.parse(castNonNull(in.readString()));
|
||||||
|
int streamKeyCount = in.readInt();
|
||||||
|
ArrayList<StreamKey> mutableStreamKeys = new ArrayList<>(streamKeyCount);
|
||||||
|
for (int i = 0; i < streamKeyCount; i++) {
|
||||||
|
mutableStreamKeys.add(in.readParcelable(StreamKey.class.getClassLoader()));
|
||||||
|
}
|
||||||
|
streamKeys = Collections.unmodifiableList(mutableStreamKeys);
|
||||||
|
customCacheKey = in.readString();
|
||||||
|
data = new byte[in.readInt()];
|
||||||
|
in.readByteArray(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy with the specified ID.
|
||||||
|
*
|
||||||
|
* @param id The ID of the copy.
|
||||||
|
* @return The copy with the specified ID.
|
||||||
|
*/
|
||||||
|
public DownloadAction copyWithId(String id) {
|
||||||
|
return new DownloadAction(id, type, uri, streamKeys, customCacheKey, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the result of merging {@code newAction} into this action.
|
* Returns the result of merging {@code newAction} into this action.
|
||||||
*
|
*
|
||||||
@ -143,7 +116,6 @@ public final class DownloadAction {
|
|||||||
public DownloadAction copyWithMergedAction(DownloadAction newAction) {
|
public DownloadAction copyWithMergedAction(DownloadAction newAction) {
|
||||||
Assertions.checkState(id.equals(newAction.id));
|
Assertions.checkState(id.equals(newAction.id));
|
||||||
Assertions.checkState(type.equals(newAction.type));
|
Assertions.checkState(type.equals(newAction.type));
|
||||||
|
|
||||||
List<StreamKey> mergedKeys;
|
List<StreamKey> mergedKeys;
|
||||||
if (streamKeys.isEmpty() || newAction.streamKeys.isEmpty()) {
|
if (streamKeys.isEmpty() || newAction.streamKeys.isEmpty()) {
|
||||||
// If either streamKeys is empty then all streams should be downloaded.
|
// If either streamKeys is empty then all streams should be downloaded.
|
||||||
@ -161,18 +133,6 @@ public final class DownloadAction {
|
|||||||
id, type, newAction.uri, mergedKeys, newAction.customCacheKey, newAction.data);
|
id, type, newAction.uri, mergedKeys, newAction.customCacheKey, newAction.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Serializes itself into a byte array. */
|
|
||||||
public byte[] toByteArray() {
|
|
||||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
||||||
try {
|
|
||||||
serializeToStream(output);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// ByteArrayOutputStream shouldn't throw IOException.
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
return output.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
if (!(o instanceof DownloadAction)) {
|
if (!(o instanceof DownloadAction)) {
|
||||||
@ -191,6 +151,7 @@ public final class DownloadAction {
|
|||||||
public final int hashCode() {
|
public final int hashCode() {
|
||||||
int result = type.hashCode();
|
int result = type.hashCode();
|
||||||
result = 31 * result + id.hashCode();
|
result = 31 * result + id.hashCode();
|
||||||
|
result = 31 * result + type.hashCode();
|
||||||
result = 31 * result + uri.hashCode();
|
result = 31 * result + uri.hashCode();
|
||||||
result = 31 * result + streamKeys.hashCode();
|
result = 31 * result + streamKeys.hashCode();
|
||||||
result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0);
|
result = 31 * result + (customCacheKey != null ? customCacheKey.hashCode() : 0);
|
||||||
@ -198,101 +159,38 @@ public final class DownloadAction {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialization.
|
// Parcelable implementation.
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Serializes this action into an {@link OutputStream}.
|
public int describeContents() {
|
||||||
*
|
return 0;
|
||||||
* @param output The stream to write to.
|
}
|
||||||
*/
|
|
||||||
public final void serializeToStream(OutputStream output) throws IOException {
|
@Override
|
||||||
// Don't close the stream as it closes the underlying stream too.
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
DataOutputStream dataOutputStream = new DataOutputStream(output);
|
dest.writeString(id);
|
||||||
dataOutputStream.writeUTF(type);
|
dest.writeString(type);
|
||||||
dataOutputStream.writeInt(VERSION);
|
dest.writeString(uri.toString());
|
||||||
dataOutputStream.writeUTF(uri.toString());
|
dest.writeInt(streamKeys.size());
|
||||||
dataOutputStream.writeBoolean(false);
|
|
||||||
dataOutputStream.writeInt(data.length);
|
|
||||||
dataOutputStream.write(data);
|
|
||||||
dataOutputStream.writeInt(streamKeys.size());
|
|
||||||
for (int i = 0; i < streamKeys.size(); i++) {
|
for (int i = 0; i < streamKeys.size(); i++) {
|
||||||
StreamKey key = streamKeys.get(i);
|
dest.writeParcelable(streamKeys.get(i), /* parcelableFlags= */ 0);
|
||||||
dataOutputStream.writeInt(key.periodIndex);
|
|
||||||
dataOutputStream.writeInt(key.groupIndex);
|
|
||||||
dataOutputStream.writeInt(key.trackIndex);
|
|
||||||
}
|
}
|
||||||
dataOutputStream.writeBoolean(customCacheKey != null);
|
dest.writeString(customCacheKey);
|
||||||
if (customCacheKey != null) {
|
dest.writeInt(data.length);
|
||||||
dataOutputStream.writeUTF(customCacheKey);
|
dest.writeByteArray(data);
|
||||||
}
|
|
||||||
dataOutputStream.writeUTF(id);
|
|
||||||
dataOutputStream.flush();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DownloadAction readFromStream(DataInputStream input) throws IOException {
|
public static final Parcelable.Creator<DownloadAction> CREATOR =
|
||||||
String type = input.readUTF();
|
new Parcelable.Creator<DownloadAction>() {
|
||||||
int version = input.readInt();
|
|
||||||
|
|
||||||
Uri uri = Uri.parse(input.readUTF());
|
@Override
|
||||||
boolean isRemoveAction = input.readBoolean();
|
public DownloadAction createFromParcel(Parcel in) {
|
||||||
|
return new DownloadAction(in);
|
||||||
|
}
|
||||||
|
|
||||||
int dataLength = input.readInt();
|
@Override
|
||||||
byte[] data;
|
public DownloadAction[] newArray(int size) {
|
||||||
if (dataLength != 0) {
|
return new DownloadAction[size];
|
||||||
data = new byte[dataLength];
|
}
|
||||||
input.readFully(data);
|
};
|
||||||
} else {
|
|
||||||
data = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized version 0 progressive actions did not contain keys.
|
|
||||||
boolean isLegacyProgressive = version == 0 && TYPE_PROGRESSIVE.equals(type);
|
|
||||||
List<StreamKey> keys = new ArrayList<>();
|
|
||||||
if (!isLegacyProgressive) {
|
|
||||||
int keyCount = input.readInt();
|
|
||||||
for (int i = 0; i < keyCount; i++) {
|
|
||||||
keys.add(readKey(type, version, input));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized version 0 and 1 DASH/HLS/SS actions did not contain a custom cache key.
|
|
||||||
boolean isLegacySegmented =
|
|
||||||
version < 2 && (TYPE_DASH.equals(type) || TYPE_HLS.equals(type) || TYPE_SS.equals(type));
|
|
||||||
String customCacheKey = null;
|
|
||||||
if (!isLegacySegmented) {
|
|
||||||
customCacheKey = input.readBoolean() ? input.readUTF() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialized version 0, 1 and 2 did not contain an id.
|
|
||||||
String id = version < 3 ? generateId(uri, customCacheKey) : input.readUTF();
|
|
||||||
|
|
||||||
if (isRemoveAction) {
|
|
||||||
// Remove actions are not supported anymore.
|
|
||||||
throw new UnsupportedActionException();
|
|
||||||
}
|
|
||||||
return new DownloadAction(id, type, uri, keys, customCacheKey, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* package */ static String generateId(Uri uri, @Nullable String customCacheKey) {
|
|
||||||
return customCacheKey != null ? customCacheKey : uri.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StreamKey readKey(String type, int version, DataInputStream input)
|
|
||||||
throws IOException {
|
|
||||||
int periodIndex;
|
|
||||||
int groupIndex;
|
|
||||||
int trackIndex;
|
|
||||||
|
|
||||||
// Serialized version 0 HLS/SS actions did not contain a period index.
|
|
||||||
if ((TYPE_HLS.equals(type) || TYPE_SS.equals(type)) && version == 0) {
|
|
||||||
periodIndex = 0;
|
|
||||||
groupIndex = input.readInt();
|
|
||||||
trackIndex = input.readInt();
|
|
||||||
} else {
|
|
||||||
periodIndex = input.readInt();
|
|
||||||
groupIndex = input.readInt();
|
|
||||||
trackIndex = input.readInt();
|
|
||||||
}
|
|
||||||
return new StreamKey(periodIndex, groupIndex, trackIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -586,8 +586,8 @@ public final class DownloadHelper {
|
|||||||
public DownloadAction getDownloadAction(@Nullable byte[] data) {
|
public DownloadAction getDownloadAction(@Nullable byte[] data) {
|
||||||
String downloadId = uri.toString();
|
String downloadId = uri.toString();
|
||||||
if (mediaSource == null) {
|
if (mediaSource == null) {
|
||||||
return DownloadAction.createDownloadAction(
|
return new DownloadAction(
|
||||||
downloadId, downloadType, uri, /* keys= */ Collections.emptyList(), cacheKey, data);
|
downloadId, downloadType, uri, /* streamKeys= */ Collections.emptyList(), cacheKey, data);
|
||||||
}
|
}
|
||||||
assertPreparedWithMedia();
|
assertPreparedWithMedia();
|
||||||
List<StreamKey> streamKeys = new ArrayList<>();
|
List<StreamKey> streamKeys = new ArrayList<>();
|
||||||
@ -601,8 +601,7 @@ public final class DownloadHelper {
|
|||||||
}
|
}
|
||||||
streamKeys.addAll(mediaPreparer.mediaPeriods[periodIndex].getStreamKeys(allSelections));
|
streamKeys.addAll(mediaPreparer.mediaPeriods[periodIndex].getStreamKeys(allSelections));
|
||||||
}
|
}
|
||||||
return DownloadAction.createDownloadAction(
|
return new DownloadAction(downloadId, downloadType, uri, streamKeys, cacheKey, data);
|
||||||
downloadId, downloadType, uri, streamKeys, cacheKey, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialization of array of Lists.
|
// Initialization of array of Lists.
|
||||||
|
@ -36,45 +36,40 @@ public final class DownloadIndexUtil {
|
|||||||
private DownloadIndexUtil() {}
|
private DownloadIndexUtil() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrades an {@link ActionFile} to {@link DownloadIndex}.
|
* Merges {@link DownloadAction DownloadActions} contained in an {@link ActionFile} into a {@link
|
||||||
|
* DownloadIndex}.
|
||||||
*
|
*
|
||||||
* <p>This method shouldn't be called while {@link DownloadIndex} is used by {@link
|
* <p>This method must not be called while the {@link DownloadIndex} is being used by a {@link
|
||||||
* DownloadManager}.
|
* DownloadManager}.
|
||||||
*
|
*
|
||||||
* @param actionFile The action file to upgrade.
|
* @param actionFile The action file.
|
||||||
* @param downloadIndex Actions are converted to {@link DownloadState}s and stored in this index.
|
* @param downloadIdProvider A custom download id provider, or {@code null}.
|
||||||
* @param downloadIdProvider A nullable custom download id provider.
|
* @param downloadIndex The index into which the action will be merged.
|
||||||
* @throws IOException If there is an error during loading actions.
|
* @throws IOException If an error occurs loading or merging the actions.
|
||||||
*/
|
*/
|
||||||
public static void upgradeActionFile(
|
public static void mergeActionFile(
|
||||||
ActionFile actionFile,
|
ActionFile actionFile,
|
||||||
DefaultDownloadIndex downloadIndex,
|
@Nullable DownloadIdProvider downloadIdProvider,
|
||||||
@Nullable DownloadIdProvider downloadIdProvider)
|
DefaultDownloadIndex downloadIndex)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (downloadIdProvider == null) {
|
|
||||||
downloadIdProvider = downloadAction -> downloadAction.id;
|
|
||||||
}
|
|
||||||
for (DownloadAction action : actionFile.load()) {
|
for (DownloadAction action : actionFile.load()) {
|
||||||
addAction(downloadIndex, downloadIdProvider.getId(action), action);
|
if (downloadIdProvider != null) {
|
||||||
|
action = action.copyWithId(downloadIdProvider.getId(action));
|
||||||
|
}
|
||||||
|
mergeAction(action, downloadIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a {@link DownloadAction} to {@link DownloadState} and stored in the given {@link
|
* Merges a {@link DownloadAction} into a {@link DownloadIndexUtil}.
|
||||||
* DownloadIndex}.
|
|
||||||
*
|
*
|
||||||
* <p>This method shouldn't be called while {@link DownloadIndex} is used by {@link
|
* @param action The action to be merged.
|
||||||
* DownloadManager}.
|
* @param downloadIndex The index into which the action will be merged.
|
||||||
*
|
* @throws IOException If an error occurs merging the action.
|
||||||
* @param downloadIndex The action is converted to {@link DownloadState} and stored in this index.
|
|
||||||
* @param id A nullable custom download id which overwrites {@link DownloadAction#id}.
|
|
||||||
* @param action The action to be stored in {@link DownloadIndex}.
|
|
||||||
* @throws IOException If an error occurs storing the state in the {@link DownloadIndex}.
|
|
||||||
*/
|
*/
|
||||||
public static void addAction(
|
/* package */ static void mergeAction(DownloadAction action, DefaultDownloadIndex downloadIndex)
|
||||||
DefaultDownloadIndex downloadIndex, @Nullable String id, DownloadAction action)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
DownloadState downloadState = downloadIndex.getDownloadState(id != null ? id : action.id);
|
DownloadState downloadState = downloadIndex.getDownloadState(action.id);
|
||||||
if (downloadState != null) {
|
if (downloadState != null) {
|
||||||
downloadState = downloadState.copyWithMergedAction(action);
|
downloadState = downloadState.copyWithMergedAction(action);
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,7 +30,6 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.NotificationUtil;
|
import com.google.android.exoplayer2.util.NotificationUtil;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
/** A {@link Service} for downloading media. */
|
/** A {@link Service} for downloading media. */
|
||||||
@ -54,9 +53,8 @@ public abstract class DownloadService extends Service {
|
|||||||
* Adds a new download. Extras:
|
* Adds a new download. Extras:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>{@link #KEY_DOWNLOAD_ACTION} - The {@code byte[]} representation of the {@link
|
* <li>{@link #KEY_DOWNLOAD_ACTION} - A {@link DownloadAction} defining the download to be
|
||||||
* DownloadAction} that defines the download to be added. The required representation can be
|
* added.
|
||||||
* obtained by calling {@link DownloadAction#toByteArray()}.
|
|
||||||
* <li>{@link #KEY_FOREGROUND} - See {@link #KEY_FOREGROUND}.
|
* <li>{@link #KEY_FOREGROUND} - See {@link #KEY_FOREGROUND}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@ -231,7 +229,7 @@ public abstract class DownloadService extends Service {
|
|||||||
DownloadAction downloadAction,
|
DownloadAction downloadAction,
|
||||||
boolean foreground) {
|
boolean foreground) {
|
||||||
return getIntent(context, clazz, ACTION_ADD)
|
return getIntent(context, clazz, ACTION_ADD)
|
||||||
.putExtra(KEY_DOWNLOAD_ACTION, downloadAction.toByteArray())
|
.putExtra(KEY_DOWNLOAD_ACTION, downloadAction)
|
||||||
.putExtra(KEY_FOREGROUND, foreground);
|
.putExtra(KEY_FOREGROUND, foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,19 +388,11 @@ public abstract class DownloadService extends Service {
|
|||||||
// Do nothing.
|
// Do nothing.
|
||||||
break;
|
break;
|
||||||
case ACTION_ADD:
|
case ACTION_ADD:
|
||||||
byte[] actionData = intent.getByteArrayExtra(KEY_DOWNLOAD_ACTION);
|
DownloadAction downloadAction = intent.getParcelableExtra(KEY_DOWNLOAD_ACTION);
|
||||||
if (actionData == null) {
|
if (downloadAction == null) {
|
||||||
Log.e(TAG, "Ignored ADD action: Missing download_action extra");
|
Log.e(TAG, "Ignored ADD action: Missing download_action extra");
|
||||||
} else {
|
} else {
|
||||||
DownloadAction downloadAction = null;
|
downloadManager.addDownload(downloadAction);
|
||||||
try {
|
|
||||||
downloadAction = DownloadAction.fromByteArray(actionData);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(TAG, "Ignored ADD action: Failed to deserialize download_action extra", e);
|
|
||||||
}
|
|
||||||
if (downloadAction != null) {
|
|
||||||
downloadManager.addDownload(downloadAction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ACTION_START:
|
case ACTION_START:
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ import androidx.annotation.Nullable;
|
|||||||
* within the group. The interpretation of these indices depends on the type of media for which the
|
* within the group. The interpretation of these indices depends on the type of media for which the
|
||||||
* stream key is used.
|
* stream key is used.
|
||||||
*/
|
*/
|
||||||
public final class StreamKey implements Comparable<StreamKey> {
|
public final class StreamKey implements Comparable<StreamKey>, Parcelable {
|
||||||
|
|
||||||
/** The period index. */
|
/** The period index. */
|
||||||
public final int periodIndex;
|
public final int periodIndex;
|
||||||
@ -53,6 +55,12 @@ public final class StreamKey implements Comparable<StreamKey> {
|
|||||||
this.trackIndex = trackIndex;
|
this.trackIndex = trackIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package */ StreamKey(Parcel in) {
|
||||||
|
periodIndex = in.readInt();
|
||||||
|
groupIndex = in.readInt();
|
||||||
|
trackIndex = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return periodIndex + "." + groupIndex + "." + trackIndex;
|
return periodIndex + "." + groupIndex + "." + trackIndex;
|
||||||
@ -94,4 +102,32 @@ public final class StreamKey implements Comparable<StreamKey> {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parcelable implementation.
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(periodIndex);
|
||||||
|
dest.writeInt(groupIndex);
|
||||||
|
dest.writeInt(trackIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<StreamKey> CREATOR =
|
||||||
|
new Parcelable.Creator<StreamKey>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamKey createFromParcel(Parcel in) {
|
||||||
|
return new StreamKey(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamKey[] newArray(int size) {
|
||||||
|
return new StreamKey[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -124,11 +124,11 @@ public class ActionFileTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static DownloadAction buildExpectedAction(Uri uri, byte[] data) {
|
private static DownloadAction buildExpectedAction(Uri uri, byte[] data) {
|
||||||
return DownloadAction.createDownloadAction(
|
return new DownloadAction(
|
||||||
/* id= */ uri.toString(),
|
/* id= */ uri.toString(),
|
||||||
DownloadAction.TYPE_PROGRESSIVE,
|
DownloadAction.TYPE_PROGRESSIVE,
|
||||||
uri,
|
uri,
|
||||||
/* keys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
data);
|
data);
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,11 @@ public final class DefaultDownloaderFactoryTest {
|
|||||||
|
|
||||||
Downloader downloader =
|
Downloader downloader =
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_PROGRESSIVE,
|
DownloadAction.TYPE_PROGRESSIVE,
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
/* keys= */ Collections.emptyList(),
|
/* streamKeys= */ Collections.emptyList(),
|
||||||
/* customCacheKey= */ null,
|
/* customCacheKey= */ null,
|
||||||
/* data= */ null));
|
/* data= */ null));
|
||||||
assertThat(downloader).isInstanceOf(ProgressiveDownloader.class);
|
assertThat(downloader).isInstanceOf(ProgressiveDownloader.class);
|
||||||
|
@ -16,24 +16,11 @@
|
|||||||
package com.google.android.exoplayer2.offline;
|
package com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_DASH;
|
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_DASH;
|
||||||
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_HLS;
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_PROGRESSIVE;
|
|
||||||
import static com.google.android.exoplayer2.offline.DownloadAction.TYPE_SS;
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.annotation.Nullable;
|
import android.os.Parcel;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.offline.DownloadAction.UnsupportedActionException;
|
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -47,41 +34,34 @@ public class DownloadActionTest {
|
|||||||
|
|
||||||
private Uri uri1;
|
private Uri uri1;
|
||||||
private Uri uri2;
|
private Uri uri2;
|
||||||
private byte[] data;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
uri1 = Uri.parse("http://test/1.uri");
|
uri1 = Uri.parse("http://test/1.uri");
|
||||||
uri2 = Uri.parse("http://test/2.uri");
|
uri2 = Uri.parse("http://test/2.uri");
|
||||||
data = TestUtil.buildTestData(32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSameUri_hasSameId() {
|
public void testParcelable() {
|
||||||
DownloadAction action1 = createAction(uri1);
|
ArrayList<StreamKey> streamKeys = new ArrayList<>();
|
||||||
DownloadAction action2 = createAction(uri1);
|
streamKeys.add(new StreamKey(1, 2, 3));
|
||||||
assertThat(action1.id.equals(action2.id)).isTrue();
|
streamKeys.add(new StreamKey(4, 5, 6));
|
||||||
}
|
DownloadAction actionToParcel =
|
||||||
|
new DownloadAction(
|
||||||
|
"id",
|
||||||
|
"type",
|
||||||
|
Uri.parse("https://abc.def/ghi"),
|
||||||
|
streamKeys,
|
||||||
|
"key",
|
||||||
|
new byte[] {1, 2, 3, 4, 5});
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
actionToParcel.writeToParcel(parcel, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
|
||||||
@Test
|
DownloadAction actionFromParcel = DownloadAction.CREATOR.createFromParcel(parcel);
|
||||||
public void testSameUriDifferentAction_hasSameId() {
|
assertThat(actionFromParcel).isEqualTo(actionToParcel);
|
||||||
DownloadAction action1 = createAction(uri1);
|
|
||||||
DownloadAction action2 = createAction(uri1);
|
|
||||||
assertThat(action1.id.equals(action2.id)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
parcel.recycle();
|
||||||
public void testDifferentUri_IsNotSameMedia() {
|
|
||||||
DownloadAction action1 = createAction(uri1);
|
|
||||||
DownloadAction action2 = createAction(uri2);
|
|
||||||
assertThat(action1.id.equals(action2.id)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSameCacheKeyDifferentUri_hasSameId() {
|
|
||||||
DownloadAction action1 = createAction(uri1, "key123");
|
|
||||||
DownloadAction action2 = createAction(uri2, "key123");
|
|
||||||
assertThat(action1.id.equals(action2.id)).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("EqualsWithItself")
|
@SuppressWarnings("EqualsWithItself")
|
||||||
@ -119,128 +99,6 @@ public class DownloadActionTest {
|
|||||||
assertEqual(action16, action17);
|
assertEqual(action16, action17);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerWriteRead() throws Exception {
|
|
||||||
assertStreamSerializationRoundTrip(
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
"id",
|
|
||||||
TYPE_DASH,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
|
|
||||||
"key123",
|
|
||||||
data));
|
|
||||||
assertStreamSerializationRoundTrip(createAction(uri1, "key123"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testArraySerialization() throws Exception {
|
|
||||||
assertArraySerializationRoundTrip(
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
"id",
|
|
||||||
TYPE_DASH,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
|
|
||||||
"key123",
|
|
||||||
data));
|
|
||||||
assertArraySerializationRoundTrip(createAction(uri1, "key123"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerProgressiveVersion0() throws Exception {
|
|
||||||
String customCacheKey = "key123";
|
|
||||||
String expectedId = DownloadAction.generateId(uri1, customCacheKey);
|
|
||||||
assertDeserialization(
|
|
||||||
"progressive-download-v0",
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
expectedId, TYPE_PROGRESSIVE, uri1, Collections.emptyList(), customCacheKey, data));
|
|
||||||
assertUnsupportedAction("progressive-remove-v0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerDashVersion0() throws Exception {
|
|
||||||
assertDeserialization(
|
|
||||||
"dash-download-v0",
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
uri1.toString(),
|
|
||||||
TYPE_DASH,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
|
|
||||||
/* customCacheKey= */ null,
|
|
||||||
data));
|
|
||||||
assertUnsupportedAction("dash-remove-v0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerHlsVersion0() throws Exception {
|
|
||||||
assertDeserialization(
|
|
||||||
"hls-download-v0",
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
uri1.toString(),
|
|
||||||
TYPE_HLS,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1), new StreamKey(2, 3)),
|
|
||||||
/* customCacheKey= */ null,
|
|
||||||
data));
|
|
||||||
assertUnsupportedAction("hls-remove-v0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerHlsVersion1() throws Exception {
|
|
||||||
assertDeserialization(
|
|
||||||
"hls-download-v1",
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
uri1.toString(),
|
|
||||||
TYPE_HLS,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
|
|
||||||
/* customCacheKey= */ null,
|
|
||||||
data));
|
|
||||||
assertUnsupportedAction("hls-remove-v1");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerSsVersion0() throws Exception {
|
|
||||||
assertDeserialization(
|
|
||||||
"ss-download-v0",
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
uri1.toString(),
|
|
||||||
TYPE_SS,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1), new StreamKey(2, 3)),
|
|
||||||
/* customCacheKey= */ null,
|
|
||||||
data));
|
|
||||||
assertUnsupportedAction("ss-remove-v0");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSerializerSsVersion1() throws Exception {
|
|
||||||
assertDeserialization(
|
|
||||||
"ss-download-v1",
|
|
||||||
DownloadAction.createDownloadAction(
|
|
||||||
uri1.toString(),
|
|
||||||
TYPE_SS,
|
|
||||||
uri1,
|
|
||||||
toList(new StreamKey(0, 1, 2), new StreamKey(3, 4, 5)),
|
|
||||||
/* customCacheKey= */ null,
|
|
||||||
data));
|
|
||||||
assertUnsupportedAction("ss-remove-v1");
|
|
||||||
}
|
|
||||||
|
|
||||||
private DownloadAction createAction(Uri uri, StreamKey... keys) {
|
|
||||||
return DownloadAction.createDownloadAction(
|
|
||||||
uri.toString(), TYPE_DASH, uri, toList(keys), /* customCacheKey= */ null, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DownloadAction createAction(Uri uri, @Nullable String customCacheKey) {
|
|
||||||
return DownloadAction.createDownloadAction(
|
|
||||||
"id",
|
|
||||||
DownloadAction.TYPE_DASH,
|
|
||||||
uri,
|
|
||||||
Collections.emptyList(),
|
|
||||||
customCacheKey,
|
|
||||||
/* data= */ null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void assertNotEqual(DownloadAction action1, DownloadAction action2) {
|
private static void assertNotEqual(DownloadAction action1, DownloadAction action2) {
|
||||||
assertThat(action1).isNotEqualTo(action2);
|
assertThat(action1).isNotEqualTo(action2);
|
||||||
assertThat(action2).isNotEqualTo(action1);
|
assertThat(action2).isNotEqualTo(action1);
|
||||||
@ -251,42 +109,9 @@ public class DownloadActionTest {
|
|||||||
assertThat(action2).isEqualTo(action1);
|
assertThat(action2).isEqualTo(action1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertStreamSerializationRoundTrip(DownloadAction action) throws IOException {
|
private static DownloadAction createAction(Uri uri, StreamKey... keys) {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
return new DownloadAction(
|
||||||
DataOutputStream output = new DataOutputStream(out);
|
uri.toString(), TYPE_DASH, uri, toList(keys), /* customCacheKey= */ null, /* data= */ null);
|
||||||
action.serializeToStream(output);
|
|
||||||
|
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
|
||||||
DataInputStream input = new DataInputStream(in);
|
|
||||||
DownloadAction deserializedAction = DownloadAction.deserializeFromStream(input);
|
|
||||||
|
|
||||||
assertEqual(action, deserializedAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void assertArraySerializationRoundTrip(DownloadAction action) throws IOException {
|
|
||||||
assertEqual(action, DownloadAction.fromByteArray(action.toByteArray()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void assertDeserialization(String fileName, DownloadAction expectedAction)
|
|
||||||
throws IOException {
|
|
||||||
InputStream input =
|
|
||||||
TestUtil.getInputStream(
|
|
||||||
ApplicationProvider.getApplicationContext(), "download-actions/" + fileName);
|
|
||||||
DownloadAction deserializedAction = DownloadAction.deserializeFromStream(input);
|
|
||||||
|
|
||||||
assertEqual(deserializedAction, expectedAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void assertUnsupportedAction(String fileName) throws IOException {
|
|
||||||
InputStream input =
|
|
||||||
TestUtil.getInputStream(
|
|
||||||
ApplicationProvider.getApplicationContext(), "download-actions/" + fileName);
|
|
||||||
try {
|
|
||||||
DownloadAction.deserializeFromStream(input);
|
|
||||||
fail();
|
|
||||||
} catch (UnsupportedActionException e) {
|
|
||||||
// Expected exception.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<StreamKey> toList(StreamKey... keys) {
|
private static List<StreamKey> toList(StreamKey... keys) {
|
||||||
|
@ -59,7 +59,7 @@ public class DownloadIndexUtilTest {
|
|||||||
public void addAction_nonExistingDownloadState_createsNewDownloadState() throws IOException {
|
public void addAction_nonExistingDownloadState_createsNewDownloadState() throws IOException {
|
||||||
byte[] data = new byte[] {1, 2, 3, 4};
|
byte[] data = new byte[] {1, 2, 3, 4};
|
||||||
DownloadAction action =
|
DownloadAction action =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
TYPE_DASH,
|
TYPE_DASH,
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
@ -69,7 +69,7 @@ public class DownloadIndexUtilTest {
|
|||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
data);
|
data);
|
||||||
|
|
||||||
DownloadIndexUtil.addAction(downloadIndex, action.id, action);
|
DownloadIndexUtil.mergeAction(action, downloadIndex);
|
||||||
|
|
||||||
assertDownloadIndexContainsAction(action, DownloadState.STATE_QUEUED);
|
assertDownloadIndexContainsAction(action, DownloadState.STATE_QUEUED);
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ public class DownloadIndexUtilTest {
|
|||||||
StreamKey streamKey2 =
|
StreamKey streamKey2 =
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
DownloadAction action1 =
|
DownloadAction action1 =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
TYPE_DASH,
|
TYPE_DASH,
|
||||||
Uri.parse("https://www.test.com/download1"),
|
Uri.parse("https://www.test.com/download1"),
|
||||||
@ -89,16 +89,16 @@ public class DownloadIndexUtilTest {
|
|||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {1, 2, 3, 4});
|
new byte[] {1, 2, 3, 4});
|
||||||
DownloadAction action2 =
|
DownloadAction action2 =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
TYPE_DASH,
|
TYPE_DASH,
|
||||||
Uri.parse("https://www.test.com/download2"),
|
Uri.parse("https://www.test.com/download2"),
|
||||||
asList(streamKey2),
|
asList(streamKey2),
|
||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {5, 4, 3, 2, 1});
|
new byte[] {5, 4, 3, 2, 1});
|
||||||
DownloadIndexUtil.addAction(downloadIndex, action1.id, action1);
|
DownloadIndexUtil.mergeAction(action1, downloadIndex);
|
||||||
|
|
||||||
DownloadIndexUtil.addAction(downloadIndex, action2.id, action2);
|
DownloadIndexUtil.mergeAction(action2, downloadIndex);
|
||||||
|
|
||||||
DownloadState downloadState = downloadIndex.getDownloadState(action2.id);
|
DownloadState downloadState = downloadIndex.getDownloadState(action2.id);
|
||||||
assertThat(downloadState).isNotNull();
|
assertThat(downloadState).isNotNull();
|
||||||
@ -126,7 +126,7 @@ public class DownloadIndexUtilTest {
|
|||||||
StreamKey expectedStreamKey2 =
|
StreamKey expectedStreamKey2 =
|
||||||
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
DownloadAction expectedAction1 =
|
DownloadAction expectedAction1 =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"key123",
|
"key123",
|
||||||
TYPE_DASH,
|
TYPE_DASH,
|
||||||
Uri.parse("https://www.test.com/download1"),
|
Uri.parse("https://www.test.com/download1"),
|
||||||
@ -134,7 +134,7 @@ public class DownloadIndexUtilTest {
|
|||||||
/* customCacheKey= */ "key123",
|
/* customCacheKey= */ "key123",
|
||||||
new byte[] {1, 2, 3, 4});
|
new byte[] {1, 2, 3, 4});
|
||||||
DownloadAction expectedAction2 =
|
DownloadAction expectedAction2 =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"key234",
|
"key234",
|
||||||
TYPE_DASH,
|
TYPE_DASH,
|
||||||
Uri.parse("https://www.test.com/download2"),
|
Uri.parse("https://www.test.com/download2"),
|
||||||
@ -143,7 +143,7 @@ public class DownloadIndexUtilTest {
|
|||||||
new byte[] {5, 4, 3, 2, 1});
|
new byte[] {5, 4, 3, 2, 1});
|
||||||
|
|
||||||
ActionFile actionFile = new ActionFile(tempFile);
|
ActionFile actionFile = new ActionFile(tempFile);
|
||||||
DownloadIndexUtil.upgradeActionFile(actionFile, downloadIndex, /* downloadIdProvider= */ null);
|
DownloadIndexUtil.mergeActionFile(actionFile, /* downloadIdProvider= */ null, downloadIndex);
|
||||||
assertDownloadIndexContainsAction(expectedAction1, DownloadState.STATE_QUEUED);
|
assertDownloadIndexContainsAction(expectedAction1, DownloadState.STATE_QUEUED);
|
||||||
assertDownloadIndexContainsAction(expectedAction2, DownloadState.STATE_QUEUED);
|
assertDownloadIndexContainsAction(expectedAction2, DownloadState.STATE_QUEUED);
|
||||||
}
|
}
|
||||||
|
@ -507,7 +507,7 @@ public class DownloadManagerTest {
|
|||||||
|
|
||||||
private DownloadRunner postDownloadAction(StreamKey... keys) {
|
private DownloadRunner postDownloadAction(StreamKey... keys) {
|
||||||
DownloadAction downloadAction =
|
DownloadAction downloadAction =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
id,
|
id,
|
||||||
DownloadAction.TYPE_PROGRESSIVE,
|
DownloadAction.TYPE_PROGRESSIVE,
|
||||||
uri,
|
uri,
|
||||||
|
@ -153,8 +153,7 @@ class DownloadStateBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DownloadState build() {
|
public DownloadState build() {
|
||||||
DownloadAction action =
|
DownloadAction action = new DownloadAction(id, type, uri, streamKeys, cacheKey, customMetadata);
|
||||||
DownloadAction.createDownloadAction(id, type, uri, streamKeys, cacheKey, customMetadata);
|
|
||||||
return new DownloadState(
|
return new DownloadState(
|
||||||
action,
|
action,
|
||||||
state,
|
state,
|
||||||
|
@ -99,7 +99,7 @@ public class DownloadStateTest {
|
|||||||
@Test
|
@Test
|
||||||
public void mergeAction_actionHaveDifferentData_downloadStateDataIsUpdated() {
|
public void mergeAction_actionHaveDifferentData_downloadStateDataIsUpdated() {
|
||||||
DownloadAction downloadAction =
|
DownloadAction downloadAction =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_DASH,
|
DownloadAction.TYPE_DASH,
|
||||||
testUri,
|
testUri,
|
||||||
@ -246,7 +246,7 @@ public class DownloadStateTest {
|
|||||||
private void doTestMergeActionReturnsMergedKeys(
|
private void doTestMergeActionReturnsMergedKeys(
|
||||||
StreamKey[] keys1, StreamKey[] keys2, StreamKey[] expectedKeys) {
|
StreamKey[] keys1, StreamKey[] keys2, StreamKey[] expectedKeys) {
|
||||||
DownloadAction downloadAction =
|
DownloadAction downloadAction =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_DASH,
|
DownloadAction.TYPE_DASH,
|
||||||
testUri,
|
testUri,
|
||||||
@ -286,7 +286,7 @@ public class DownloadStateTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DownloadAction createDownloadAction() {
|
private DownloadAction createDownloadAction() {
|
||||||
return DownloadAction.createDownloadAction(
|
return new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_DASH,
|
DownloadAction.TYPE_DASH,
|
||||||
testUri,
|
testUri,
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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 com.google.android.exoplayer2.offline;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit tests for {@link StreamKey}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class StreamKeyTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParcelable() {
|
||||||
|
StreamKey streamKeyToParcel = new StreamKey(1, 2, 3);
|
||||||
|
Parcel parcel = Parcel.obtain();
|
||||||
|
streamKeyToParcel.writeToParcel(parcel, 0);
|
||||||
|
parcel.setDataPosition(0);
|
||||||
|
|
||||||
|
StreamKey streamKeyFromParcel = StreamKey.CREATOR.createFromParcel(parcel);
|
||||||
|
assertThat(streamKeyFromParcel).isEqualTo(streamKeyToParcel);
|
||||||
|
|
||||||
|
parcel.recycle();
|
||||||
|
}
|
||||||
|
}
|
@ -84,7 +84,7 @@ public class DashDownloaderTest {
|
|||||||
|
|
||||||
Downloader downloader =
|
Downloader downloader =
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_DASH,
|
DownloadAction.TYPE_DASH,
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
@ -232,7 +232,7 @@ public class DownloadManagerDashTest {
|
|||||||
ArrayList<StreamKey> keysList = new ArrayList<>();
|
ArrayList<StreamKey> keysList = new ArrayList<>();
|
||||||
Collections.addAll(keysList, keys);
|
Collections.addAll(keysList, keys);
|
||||||
DownloadAction action =
|
DownloadAction action =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
TEST_ID,
|
TEST_ID,
|
||||||
DownloadAction.TYPE_DASH,
|
DownloadAction.TYPE_DASH,
|
||||||
TEST_MPD_URI,
|
TEST_MPD_URI,
|
||||||
|
@ -208,7 +208,7 @@ public class DownloadServiceDashTest {
|
|||||||
ArrayList<StreamKey> keysList = new ArrayList<>();
|
ArrayList<StreamKey> keysList = new ArrayList<>();
|
||||||
Collections.addAll(keysList, keys);
|
Collections.addAll(keysList, keys);
|
||||||
DownloadAction action =
|
DownloadAction action =
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
TEST_ID,
|
TEST_ID,
|
||||||
DownloadAction.TYPE_DASH,
|
DownloadAction.TYPE_DASH,
|
||||||
TEST_MPD_URI,
|
TEST_MPD_URI,
|
||||||
|
@ -101,7 +101,7 @@ public class HlsDownloaderTest {
|
|||||||
|
|
||||||
Downloader downloader =
|
Downloader downloader =
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_HLS,
|
DownloadAction.TYPE_HLS,
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
@ -44,7 +44,7 @@ public final class SsDownloaderTest {
|
|||||||
|
|
||||||
Downloader downloader =
|
Downloader downloader =
|
||||||
factory.createDownloader(
|
factory.createDownloader(
|
||||||
DownloadAction.createDownloadAction(
|
new DownloadAction(
|
||||||
"id",
|
"id",
|
||||||
DownloadAction.TYPE_SS,
|
DownloadAction.TYPE_SS,
|
||||||
Uri.parse("https://www.test.com/download"),
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user