mirror of
https://github.com/androidx/media.git
synced 2025-05-13 02:29:52 +08:00
Add DownloadIndexUtil
This class includes helper methods to upgrade ActionFiles and custom download records to DownloadIndex. PiperOrigin-RevId: 229744441
This commit is contained in:
parent
16a185de1d
commit
f2139d1b71
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.support.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.offline.DownloadState.State;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
|
/** {@link DownloadIndex} related utility methods. */
|
||||||
|
public final class DownloadIndexUtil {
|
||||||
|
|
||||||
|
/** An interface to provide custom download ids during ActionFile upgrade. */
|
||||||
|
public interface DownloadIdProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a custom download id for given action.
|
||||||
|
*
|
||||||
|
* @param downloadAction The action which is an id requested for.
|
||||||
|
* @return A custom download id for given action.
|
||||||
|
*/
|
||||||
|
String getId(DownloadAction downloadAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DownloadIndexUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrades an {@link ActionFile} to {@link DownloadIndex}.
|
||||||
|
*
|
||||||
|
* <p>This method shouldn't be called while {@link DownloadIndex} is used by {@link
|
||||||
|
* DownloadManager}.
|
||||||
|
*
|
||||||
|
* @param actionFile The action file to upgrade.
|
||||||
|
* @param downloadIndex Actions are converted to {@link DownloadState}s and stored in this index.
|
||||||
|
* @param downloadIdProvider A nullable custom download id provider.
|
||||||
|
* @throws IOException If there is an error during loading actions.
|
||||||
|
*/
|
||||||
|
public static void upgradeActionFile(
|
||||||
|
ActionFile actionFile,
|
||||||
|
DownloadIndex downloadIndex,
|
||||||
|
@Nullable DownloadIdProvider downloadIdProvider)
|
||||||
|
throws IOException {
|
||||||
|
if (downloadIdProvider == null) {
|
||||||
|
downloadIdProvider = downloadAction -> downloadAction.id;
|
||||||
|
}
|
||||||
|
for (DownloadAction action : actionFile.load()) {
|
||||||
|
addAction(downloadIndex, downloadIdProvider.getId(action), action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a {@link DownloadAction} to {@link DownloadState} and stored in the given {@link
|
||||||
|
* DownloadIndex}.
|
||||||
|
*
|
||||||
|
* <p>This method shouldn't be called while {@link DownloadIndex} is used by {@link
|
||||||
|
* DownloadManager}.
|
||||||
|
*
|
||||||
|
* @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}.
|
||||||
|
*/
|
||||||
|
public static void addAction(
|
||||||
|
DownloadIndex downloadIndex, @Nullable String id, DownloadAction action) {
|
||||||
|
DownloadState downloadState = downloadIndex.getDownloadState(id != null ? id : action.id);
|
||||||
|
if (downloadState != null) {
|
||||||
|
downloadState = merge(downloadState, action);
|
||||||
|
} else {
|
||||||
|
downloadState = convert(action);
|
||||||
|
}
|
||||||
|
downloadIndex.putDownloadState(downloadState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DownloadState merge(DownloadState downloadState, DownloadAction action) {
|
||||||
|
Assertions.checkArgument(action.type.equals(downloadState.type));
|
||||||
|
@State int newState;
|
||||||
|
if (action.isRemoveAction) {
|
||||||
|
newState = DownloadState.STATE_REMOVING;
|
||||||
|
} else {
|
||||||
|
if (downloadState.state == DownloadState.STATE_REMOVING
|
||||||
|
|| downloadState.state == DownloadState.STATE_RESTARTING) {
|
||||||
|
newState = DownloadState.STATE_RESTARTING;
|
||||||
|
} else if (downloadState.state == DownloadState.STATE_STOPPED) {
|
||||||
|
newState = DownloadState.STATE_STOPPED;
|
||||||
|
} else {
|
||||||
|
newState = DownloadState.STATE_QUEUED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HashSet<StreamKey> keys = new HashSet<>(action.keys);
|
||||||
|
Collections.addAll(keys, downloadState.streamKeys);
|
||||||
|
StreamKey[] newKeys = keys.toArray(new StreamKey[0]);
|
||||||
|
return new DownloadState(
|
||||||
|
downloadState.id,
|
||||||
|
downloadState.type,
|
||||||
|
action.uri,
|
||||||
|
action.customCacheKey,
|
||||||
|
newState,
|
||||||
|
/* downloadPercentage= */ C.PERCENTAGE_UNSET,
|
||||||
|
downloadState.downloadedBytes,
|
||||||
|
/* totalBytes= */ C.LENGTH_UNSET,
|
||||||
|
downloadState.failureReason,
|
||||||
|
downloadState.stopFlags,
|
||||||
|
downloadState.startTimeMs,
|
||||||
|
downloadState.updateTimeMs,
|
||||||
|
newKeys,
|
||||||
|
action.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DownloadState convert(DownloadAction action) {
|
||||||
|
long currentTimeMs = System.currentTimeMillis();
|
||||||
|
return new DownloadState(
|
||||||
|
action.id,
|
||||||
|
action.type,
|
||||||
|
action.uri,
|
||||||
|
action.customCacheKey,
|
||||||
|
/* state= */ action.isRemoveAction
|
||||||
|
? DownloadState.STATE_REMOVING
|
||||||
|
: DownloadState.STATE_QUEUED,
|
||||||
|
/* downloadPercentage= */ C.PERCENTAGE_UNSET,
|
||||||
|
/* downloadedBytes= */ 0,
|
||||||
|
/* totalBytes= */ C.LENGTH_UNSET,
|
||||||
|
DownloadState.FAILURE_REASON_NONE,
|
||||||
|
/* stopFlags= */ 0,
|
||||||
|
/* startTimeMs= */ currentTimeMs,
|
||||||
|
/* updateTimeMs= */ currentTimeMs,
|
||||||
|
action.keys.toArray(new StreamKey[0]),
|
||||||
|
action.data);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* 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.android.exoplayer2.offline.DownloadAction.TYPE_DASH;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
|
/** Unit tests for {@link DownloadIndexUtil}. */
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class DownloadIndexUtilTest {
|
||||||
|
|
||||||
|
private DefaultDownloadIndex downloadIndex;
|
||||||
|
private File tempFile;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
tempFile = Util.createTempFile(RuntimeEnvironment.application, "ExoPlayerTest");
|
||||||
|
downloadIndex = new DefaultDownloadIndex(RuntimeEnvironment.application);
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
downloadIndex.release();
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addAction_nonExistingDownloadState_createsNewDownloadState() {
|
||||||
|
byte[] data = new byte[] {1, 2, 3, 4};
|
||||||
|
DownloadAction action =
|
||||||
|
DownloadAction.createDownloadAction(
|
||||||
|
TYPE_DASH,
|
||||||
|
Uri.parse("https://www.test.com/download"),
|
||||||
|
asList(
|
||||||
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2),
|
||||||
|
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5)),
|
||||||
|
/* customCacheKey= */ "key123",
|
||||||
|
data);
|
||||||
|
|
||||||
|
DownloadIndexUtil.addAction(downloadIndex, action.id, action);
|
||||||
|
|
||||||
|
assertDownloadIndexContainsAction(action, DownloadState.STATE_QUEUED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addAction_existingDownloadState_createsMergedDownloadState() {
|
||||||
|
StreamKey streamKey1 =
|
||||||
|
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5);
|
||||||
|
StreamKey streamKey2 =
|
||||||
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
|
DownloadAction action1 =
|
||||||
|
DownloadAction.createDownloadAction(
|
||||||
|
TYPE_DASH,
|
||||||
|
Uri.parse("https://www.test.com/download1"),
|
||||||
|
asList(streamKey1),
|
||||||
|
/* customCacheKey= */ "key123",
|
||||||
|
new byte[] {1, 2, 3, 4});
|
||||||
|
DownloadAction action2 =
|
||||||
|
DownloadAction.createDownloadAction(
|
||||||
|
TYPE_DASH,
|
||||||
|
Uri.parse("https://www.test.com/download2"),
|
||||||
|
asList(streamKey2),
|
||||||
|
/* customCacheKey= */ "key123",
|
||||||
|
new byte[] {5, 4, 3, 2, 1});
|
||||||
|
DownloadIndexUtil.addAction(downloadIndex, action1.id, action1);
|
||||||
|
|
||||||
|
DownloadIndexUtil.addAction(downloadIndex, action2.id, action2);
|
||||||
|
|
||||||
|
DownloadState downloadState = downloadIndex.getDownloadState(action2.id);
|
||||||
|
assertThat(downloadState).isNotNull();
|
||||||
|
assertThat(downloadState.type).isEqualTo(action2.type);
|
||||||
|
assertThat(downloadState.cacheKey).isEqualTo(action2.customCacheKey);
|
||||||
|
assertThat(downloadState.customMetadata).isEqualTo(action2.data);
|
||||||
|
assertThat(downloadState.uri).isEqualTo(action2.uri);
|
||||||
|
assertThat(downloadState.streamKeys).isEqualTo(new StreamKey[] {streamKey2, streamKey1});
|
||||||
|
assertThat(downloadState.state).isEqualTo(DownloadState.STATE_QUEUED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void upgradeActionFile_createsDownloadStates() throws Exception {
|
||||||
|
ActionFile actionFile = new ActionFile(tempFile);
|
||||||
|
StreamKey streamKey1 =
|
||||||
|
new StreamKey(/* periodIndex= */ 3, /* groupIndex= */ 4, /* trackIndex= */ 5);
|
||||||
|
StreamKey streamKey2 =
|
||||||
|
new StreamKey(/* periodIndex= */ 0, /* groupIndex= */ 1, /* trackIndex= */ 2);
|
||||||
|
DownloadAction action1 =
|
||||||
|
DownloadAction.createDownloadAction(
|
||||||
|
TYPE_DASH,
|
||||||
|
Uri.parse("https://www.test.com/download1"),
|
||||||
|
asList(streamKey1),
|
||||||
|
/* customCacheKey= */ "key123",
|
||||||
|
new byte[] {1, 2, 3, 4});
|
||||||
|
DownloadAction action2 =
|
||||||
|
DownloadAction.createDownloadAction(
|
||||||
|
TYPE_DASH,
|
||||||
|
Uri.parse("https://www.test.com/download2"),
|
||||||
|
asList(streamKey2),
|
||||||
|
/* customCacheKey= */ "key234",
|
||||||
|
new byte[] {5, 4, 3, 2, 1});
|
||||||
|
actionFile.store(action1, action2);
|
||||||
|
DownloadAction action3 =
|
||||||
|
DownloadAction.createRemoveAction(
|
||||||
|
TYPE_DASH, Uri.parse("https://www.test.com/download3"), /* customCacheKey= */ "key345");
|
||||||
|
actionFile.store(action1, action2, action3);
|
||||||
|
|
||||||
|
DownloadIndexUtil.upgradeActionFile(actionFile, downloadIndex, /* downloadIdProvider= */ null);
|
||||||
|
|
||||||
|
assertDownloadIndexContainsAction(action1, DownloadState.STATE_QUEUED);
|
||||||
|
assertDownloadIndexContainsAction(action2, DownloadState.STATE_QUEUED);
|
||||||
|
assertDownloadIndexContainsAction(action3, DownloadState.STATE_REMOVING);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDownloadIndexContainsAction(DownloadAction action, int state) {
|
||||||
|
DownloadState downloadState = downloadIndex.getDownloadState(action.id);
|
||||||
|
assertThat(downloadState).isNotNull();
|
||||||
|
assertThat(downloadState.type).isEqualTo(action.type);
|
||||||
|
assertThat(downloadState.cacheKey).isEqualTo(action.customCacheKey);
|
||||||
|
assertThat(downloadState.customMetadata).isEqualTo(action.data);
|
||||||
|
assertThat(downloadState.uri).isEqualTo(action.uri);
|
||||||
|
assertThat(downloadState.streamKeys).isEqualTo(action.keys.toArray(new StreamKey[0]));
|
||||||
|
assertThat(downloadState.state).isEqualTo(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static List<StreamKey> asList(StreamKey... streamKeys) {
|
||||||
|
return Arrays.asList(streamKeys);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user