Improve offline support in the demo app
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=195593235
@ -75,8 +75,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.google.android.exoplayer2.demo.DownloadActivity"/>
|
||||
|
||||
<service android:name="com.google.android.exoplayer2.demo.DemoDownloadService"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
|
@ -43,6 +43,7 @@ import java.io.File;
|
||||
public class DemoApplication extends Application {
|
||||
|
||||
private static final String DOWNLOAD_ACTION_FILE = "actions";
|
||||
private static final String DOWNLOAD_TRACKER_ACTION_FILE = "tracked_actions";
|
||||
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
|
||||
private static final int MAX_SIMULTANEOUS_DOWNLOADS = 2;
|
||||
private static final Deserializer[] DOWNLOAD_DESERIALIZERS =
|
||||
@ -58,6 +59,7 @@ public class DemoApplication extends Application {
|
||||
private File downloadDirectory;
|
||||
private Cache downloadCache;
|
||||
private DownloadManager downloadManager;
|
||||
private DownloadTracker downloadTracker;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@ -83,20 +85,36 @@ public class DemoApplication extends Application {
|
||||
return "withExtensions".equals(BuildConfig.FLAVOR);
|
||||
}
|
||||
|
||||
/** Returns the download manager used by the application. */
|
||||
public synchronized DownloadManager getDownloadManager() {
|
||||
public DownloadManager getDownloadManager() {
|
||||
initDownloadManager();
|
||||
return downloadManager;
|
||||
}
|
||||
|
||||
public DownloadTracker getDownloadTracker() {
|
||||
initDownloadManager();
|
||||
return downloadTracker;
|
||||
}
|
||||
|
||||
private synchronized void initDownloadManager() {
|
||||
if (downloadManager == null) {
|
||||
DownloaderConstructorHelper constructorHelper =
|
||||
new DownloaderConstructorHelper(getDownloadCache(), buildHttpDataSourceFactory(null));
|
||||
DownloaderConstructorHelper downloaderConstructorHelper =
|
||||
new DownloaderConstructorHelper(
|
||||
getDownloadCache(), buildHttpDataSourceFactory(/* listener= */ null));
|
||||
downloadManager =
|
||||
new DownloadManager(
|
||||
constructorHelper,
|
||||
downloaderConstructorHelper,
|
||||
MAX_SIMULTANEOUS_DOWNLOADS,
|
||||
DownloadManager.DEFAULT_MIN_RETRY_COUNT,
|
||||
new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE),
|
||||
DOWNLOAD_DESERIALIZERS);
|
||||
downloadTracker =
|
||||
new DownloadTracker(
|
||||
/* context= */ this,
|
||||
buildDataSourceFactory(/* listener= */ null),
|
||||
new File(getDownloadDirectory(), DOWNLOAD_TRACKER_ACTION_FILE),
|
||||
DOWNLOAD_DESERIALIZERS);
|
||||
downloadManager.addListener(downloadTracker);
|
||||
}
|
||||
return downloadManager;
|
||||
}
|
||||
|
||||
private synchronized Cache getDownloadCache() {
|
||||
|
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.demo;
|
||||
|
||||
import static com.google.android.exoplayer2.demo.PlayerActivity.EXTENSION_EXTRA;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.offline.DownloadAction;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
|
||||
import com.google.android.exoplayer2.offline.TrackKey;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloadHelper;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadHelper;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadHelper;
|
||||
import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
|
||||
import com.google.android.exoplayer2.ui.TrackNameProvider;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.util.ParcelableArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** An activity for downloading media. */
|
||||
public class DownloadActivity extends Activity {
|
||||
|
||||
public static final String PLAYER_INTENT = "player_intent";
|
||||
public static final String SAMPLE_NAME = "sample_name";
|
||||
|
||||
private Intent playerIntent;
|
||||
private String sampleName;
|
||||
|
||||
private TrackNameProvider trackNameProvider;
|
||||
private DownloadHelper downloadHelper;
|
||||
private ListView trackList;
|
||||
private ArrayAdapter<String> arrayAdapter;
|
||||
private ArrayList<TrackKey> trackKeys;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.downloader_activity);
|
||||
trackNameProvider = new DefaultTrackNameProvider(getResources());
|
||||
|
||||
Intent intent = getIntent();
|
||||
playerIntent = intent.getParcelableExtra(PLAYER_INTENT);
|
||||
Uri sampleUri = playerIntent.getData();
|
||||
sampleName = intent.getStringExtra(SAMPLE_NAME);
|
||||
getActionBar().setTitle(sampleName);
|
||||
|
||||
arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice);
|
||||
trackList = findViewById(R.id.representation_list);
|
||||
trackList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
trackList.setAdapter(arrayAdapter);
|
||||
trackKeys = new ArrayList<>();
|
||||
|
||||
DemoApplication application = (DemoApplication) getApplication();
|
||||
DataSource.Factory manifestDataSourceFactory =
|
||||
application.buildDataSourceFactory(/* listener= */ null);
|
||||
String extension = playerIntent.getStringExtra(EXTENSION_EXTRA);
|
||||
int type = Util.inferContentType(sampleUri, extension);
|
||||
switch (type) {
|
||||
case C.TYPE_DASH:
|
||||
downloadHelper = new DashDownloadHelper(sampleUri, manifestDataSourceFactory);
|
||||
break;
|
||||
case C.TYPE_SS:
|
||||
downloadHelper = new SsDownloadHelper(sampleUri, manifestDataSourceFactory);
|
||||
break;
|
||||
case C.TYPE_HLS:
|
||||
downloadHelper = new HlsDownloadHelper(sampleUri, manifestDataSourceFactory);
|
||||
break;
|
||||
case C.TYPE_OTHER:
|
||||
downloadHelper = new ProgressiveDownloadHelper(sampleUri);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
}
|
||||
|
||||
downloadHelper.prepare(
|
||||
new DownloadHelper.Callback() {
|
||||
@Override
|
||||
public void onPrepared(DownloadHelper helper) {
|
||||
DownloadActivity.this.onPrepared();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareError(DownloadHelper helper, IOException e) {
|
||||
DownloadActivity.this.onPrepareError();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onPrepared() {
|
||||
for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
|
||||
TrackGroupArray trackGroups = downloadHelper.getTrackGroups(i);
|
||||
for (int j = 0; j < trackGroups.length; j++) {
|
||||
TrackGroup trackGroup = trackGroups.get(j);
|
||||
for (int k = 0; k < trackGroup.length; k++) {
|
||||
arrayAdapter.add(trackNameProvider.getTrackName(trackGroup.getFormat(k)));
|
||||
trackKeys.add(new TrackKey(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onPrepareError() {
|
||||
Toast.makeText(
|
||||
getApplicationContext(), R.string.download_manifest_load_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
// This method is referenced in the layout file
|
||||
public void onClick(View v) {
|
||||
// switch-case doesn't work as in some compile configurations id definitions aren't constant
|
||||
int id = v.getId();
|
||||
if (id == R.id.download_button) {
|
||||
startDownload();
|
||||
} else if (id == R.id.remove_all_button) {
|
||||
removeDownload();
|
||||
} else if (id == R.id.play_button) {
|
||||
playDownload();
|
||||
}
|
||||
}
|
||||
|
||||
private void startDownload() {
|
||||
List<TrackKey> selectedTrackKeys = getSelectedTrackKeys();
|
||||
if (trackKeys.isEmpty() || !selectedTrackKeys.isEmpty()) {
|
||||
DownloadService.addDownloadAction(
|
||||
this,
|
||||
DemoDownloadService.class,
|
||||
downloadHelper.getDownloadAction(Util.getUtf8Bytes(sampleName), selectedTrackKeys));
|
||||
}
|
||||
}
|
||||
|
||||
private void removeDownload() {
|
||||
DownloadService.addDownloadAction(
|
||||
this,
|
||||
DemoDownloadService.class,
|
||||
downloadHelper.getRemoveAction(Util.getUtf8Bytes(sampleName)));
|
||||
for (int i = 0; i < trackList.getChildCount(); i++) {
|
||||
trackList.setItemChecked(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void playDownload() {
|
||||
DownloadAction action = downloadHelper.getDownloadAction(null, getSelectedTrackKeys());
|
||||
List<? extends ParcelableArray> keys = null;
|
||||
if (action instanceof SegmentDownloadAction) {
|
||||
keys = ((SegmentDownloadAction) action).keys;
|
||||
}
|
||||
if (keys.isEmpty()) {
|
||||
playerIntent.removeExtra(PlayerActivity.MANIFEST_FILTER_EXTRA);
|
||||
} else {
|
||||
playerIntent.putExtra(
|
||||
PlayerActivity.MANIFEST_FILTER_EXTRA,
|
||||
new ParcelableArray(keys.toArray(new Parcelable[0])));
|
||||
}
|
||||
startActivity(playerIntent);
|
||||
}
|
||||
|
||||
private List<TrackKey> getSelectedTrackKeys() {
|
||||
ArrayList<TrackKey> selectedTrackKeys = new ArrayList<>();
|
||||
for (int i = 0; i < trackList.getChildCount(); i++) {
|
||||
if (trackList.isItemChecked(i)) {
|
||||
selectedTrackKeys.add(trackKeys.get(i));
|
||||
}
|
||||
}
|
||||
return selectedTrackKeys;
|
||||
}
|
||||
}
|
@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.demo;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.offline.ActionFile;
|
||||
import com.google.android.exoplayer2.offline.DownloadAction;
|
||||
import com.google.android.exoplayer2.offline.DownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.TaskState;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloadHelper;
|
||||
import com.google.android.exoplayer2.offline.SegmentDownloadAction;
|
||||
import com.google.android.exoplayer2.offline.TrackKey;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloadHelper;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadHelper;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadHelper;
|
||||
import com.google.android.exoplayer2.ui.DefaultTrackNameProvider;
|
||||
import com.google.android.exoplayer2.ui.TrackNameProvider;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
/**
|
||||
* Tracks media that has been downloaded.
|
||||
*
|
||||
* <p>Tracked downloads are persisted using an {@link ActionFile}, however in a real application
|
||||
* it's expected that state will be stored directly in the application's media database, so that it
|
||||
* can be queried efficiently together with other information about the media.
|
||||
*/
|
||||
public class DownloadTracker implements DownloadManager.Listener {
|
||||
|
||||
/** Listens for changes in the tracked downloads. */
|
||||
public interface Listener {
|
||||
|
||||
/** Called when the tracked downloads changed. */
|
||||
void onDownloadsChanged();
|
||||
}
|
||||
|
||||
private static final String TAG = "DownloadTracker";
|
||||
|
||||
private final Context context;
|
||||
private final DataSource.Factory dataSourceFactory;
|
||||
private final TrackNameProvider trackNameProvider;
|
||||
private final CopyOnWriteArraySet<Listener> listeners;
|
||||
private final HashMap<Uri, DownloadAction> trackedDownloadStates;
|
||||
private final ActionFile actionFile;
|
||||
private final Handler actionFileWriteHandler;
|
||||
|
||||
public DownloadTracker(
|
||||
Context context,
|
||||
DataSource.Factory dataSourceFactory,
|
||||
File actionFile,
|
||||
DownloadAction.Deserializer[] deserializers) {
|
||||
this.context = context.getApplicationContext();
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
this.actionFile = new ActionFile(actionFile);
|
||||
trackNameProvider = new DefaultTrackNameProvider(context.getResources());
|
||||
listeners = new CopyOnWriteArraySet<>();
|
||||
trackedDownloadStates = new HashMap<>();
|
||||
HandlerThread actionFileWriteThread = new HandlerThread("DownloadTracker");
|
||||
actionFileWriteThread.start();
|
||||
actionFileWriteHandler = new Handler(actionFileWriteThread.getLooper());
|
||||
loadTrackedActions(deserializers);
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean isDownloaded(Uri uri) {
|
||||
return trackedDownloadStates.containsKey(uri);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K> List<K> getOfflineStreamKeys(Uri uri) {
|
||||
if (!trackedDownloadStates.containsKey(uri)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
DownloadAction action = trackedDownloadStates.get(uri);
|
||||
if (action instanceof SegmentDownloadAction) {
|
||||
return ((SegmentDownloadAction) action).keys;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public void toggleDownload(Activity activity, String name, Uri uri, String extension) {
|
||||
if (isDownloaded(uri)) {
|
||||
DownloadAction removeAction =
|
||||
getDownloadHelper(uri, extension).getRemoveAction(Util.getUtf8Bytes(name));
|
||||
addDownloadAction(removeAction);
|
||||
} else {
|
||||
StartDownloadDialogHelper helper =
|
||||
new StartDownloadDialogHelper(activity, getDownloadHelper(uri, extension), name);
|
||||
helper.prepare();
|
||||
}
|
||||
}
|
||||
|
||||
// DownloadManager.Listener
|
||||
|
||||
@Override
|
||||
public void onTaskStateChanged(DownloadManager downloadManager, TaskState taskState) {
|
||||
DownloadAction action = taskState.action;
|
||||
Uri uri = action.uri;
|
||||
if ((action.isRemoveAction && taskState.state == TaskState.STATE_COMPLETED)
|
||||
|| (!action.isRemoveAction && taskState.state == TaskState.STATE_FAILED)) {
|
||||
// A download has been removed, or has failed. Stop tracking it.
|
||||
if (trackedDownloadStates.remove(uri) != null) {
|
||||
handleTrackedDownloadStatesChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIdle(DownloadManager downloadManager) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
private void loadTrackedActions(DownloadAction.Deserializer[] deserializers) {
|
||||
try {
|
||||
DownloadAction[] allActions = actionFile.load(deserializers);
|
||||
for (DownloadAction action : allActions) {
|
||||
trackedDownloadStates.put(action.uri, action);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to load tracked actions", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleTrackedDownloadStatesChanged() {
|
||||
for (Listener listener : listeners) {
|
||||
listener.onDownloadsChanged();
|
||||
}
|
||||
final DownloadAction[] actions = trackedDownloadStates.values().toArray(new DownloadAction[0]);
|
||||
actionFileWriteHandler.post(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
actionFile.store(actions);
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to store tracked actions", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startDownload(DownloadAction action) {
|
||||
if (trackedDownloadStates.containsKey(action.uri)) {
|
||||
// This content is already being downloaded. Do nothing.
|
||||
return;
|
||||
}
|
||||
trackedDownloadStates.put(action.uri, action);
|
||||
handleTrackedDownloadStatesChanged();
|
||||
addDownloadAction(action);
|
||||
}
|
||||
|
||||
private void addDownloadAction(DownloadAction action) {
|
||||
DownloadService.addDownloadAction(context, DemoDownloadService.class, action);
|
||||
}
|
||||
|
||||
private DownloadHelper getDownloadHelper(Uri uri, String extension) {
|
||||
int type = Util.inferContentType(uri, extension);
|
||||
switch (type) {
|
||||
case C.TYPE_DASH:
|
||||
return new DashDownloadHelper(uri, dataSourceFactory);
|
||||
case C.TYPE_SS:
|
||||
return new SsDownloadHelper(uri, dataSourceFactory);
|
||||
case C.TYPE_HLS:
|
||||
return new HlsDownloadHelper(uri, dataSourceFactory);
|
||||
case C.TYPE_OTHER:
|
||||
return new ProgressiveDownloadHelper(uri);
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private final class StartDownloadDialogHelper
|
||||
implements DownloadHelper.Callback, DialogInterface.OnClickListener {
|
||||
|
||||
private final DownloadHelper downloadHelper;
|
||||
private final String name;
|
||||
|
||||
private final AlertDialog.Builder builder;
|
||||
private final View dialogView;
|
||||
private final List<TrackKey> trackKeys;
|
||||
private final ArrayAdapter<String> trackTitles;
|
||||
private final ListView representationList;
|
||||
|
||||
public StartDownloadDialogHelper(
|
||||
Activity activity, DownloadHelper downloadHelper, String name) {
|
||||
this.downloadHelper = downloadHelper;
|
||||
this.name = name;
|
||||
builder =
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.exo_download_description)
|
||||
.setPositiveButton(android.R.string.ok, this)
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
|
||||
// Inflate with the builder's context to ensure the correct style is used.
|
||||
LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
|
||||
dialogView = dialogInflater.inflate(R.layout.start_download_dialog, null);
|
||||
|
||||
trackKeys = new ArrayList<>();
|
||||
trackTitles =
|
||||
new ArrayAdapter<>(
|
||||
builder.getContext(), android.R.layout.simple_list_item_multiple_choice);
|
||||
representationList = dialogView.findViewById(R.id.representation_list);
|
||||
representationList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
representationList.setAdapter(trackTitles);
|
||||
}
|
||||
|
||||
public void prepare() {
|
||||
downloadHelper.prepare(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepared(DownloadHelper helper) {
|
||||
for (int i = 0; i < downloadHelper.getPeriodCount(); i++) {
|
||||
TrackGroupArray trackGroups = downloadHelper.getTrackGroups(i);
|
||||
for (int j = 0; j < trackGroups.length; j++) {
|
||||
TrackGroup trackGroup = trackGroups.get(j);
|
||||
for (int k = 0; k < trackGroup.length; k++) {
|
||||
trackKeys.add(new TrackKey(i, j, k));
|
||||
trackTitles.add(trackNameProvider.getTrackName(trackGroup.getFormat(k)));
|
||||
}
|
||||
}
|
||||
if (!trackKeys.isEmpty()) {
|
||||
builder.setView(dialogView);
|
||||
}
|
||||
builder.create().show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPrepareError(DownloadHelper helper, IOException e) {
|
||||
Toast.makeText(
|
||||
context.getApplicationContext(), R.string.download_start_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
ArrayList<TrackKey> selectedTrackKeys = new ArrayList<>();
|
||||
for (int i = 0; i < representationList.getChildCount(); i++) {
|
||||
if (representationList.isItemChecked(i)) {
|
||||
selectedTrackKeys.add(trackKeys.get(i));
|
||||
}
|
||||
}
|
||||
if (!selectedTrackKeys.isEmpty() || trackKeys.isEmpty()) {
|
||||
// We have selected keys, or we're dealing with single stream content.
|
||||
DownloadAction downloadAction =
|
||||
downloadHelper.getDownloadAction(Util.getUtf8Bytes(name), selectedTrackKeys);
|
||||
startDownload(downloadAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.Pair;
|
||||
@ -83,7 +82,6 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
|
||||
import com.google.android.exoplayer2.upstream.HttpDataSource;
|
||||
import com.google.android.exoplayer2.util.ErrorMessageProvider;
|
||||
import com.google.android.exoplayer2.util.EventLogger;
|
||||
import com.google.android.exoplayer2.util.ParcelableArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.net.CookieHandler;
|
||||
@ -104,13 +102,11 @@ public class PlayerActivity extends Activity
|
||||
|
||||
public static final String ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
|
||||
public static final String EXTENSION_EXTRA = "extension";
|
||||
public static final String MANIFEST_FILTER_EXTRA = "manifest_filter";
|
||||
|
||||
public static final String ACTION_VIEW_LIST =
|
||||
"com.google.android.exoplayer.demo.action.VIEW_LIST";
|
||||
public static final String URI_LIST_EXTRA = "uri_list";
|
||||
public static final String EXTENSION_LIST_EXTRA = "extension_list";
|
||||
public static final String MANIFEST_FILTER_LIST_EXTRA = "manifest_filter_list";
|
||||
|
||||
public static final String AD_TAG_URI_EXTRA = "ad_tag_uri";
|
||||
|
||||
@ -313,11 +309,9 @@ public class PlayerActivity extends Activity
|
||||
String action = intent.getAction();
|
||||
Uri[] uris;
|
||||
String[] extensions;
|
||||
Parcelable[] manifestFilters;
|
||||
if (ACTION_VIEW.equals(action)) {
|
||||
uris = new Uri[] {intent.getData()};
|
||||
extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)};
|
||||
manifestFilters = new Parcelable[] {intent.getParcelableExtra(MANIFEST_FILTER_EXTRA)};
|
||||
} else if (ACTION_VIEW_LIST.equals(action)) {
|
||||
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
|
||||
uris = new Uri[uriStrings.length];
|
||||
@ -328,10 +322,6 @@ public class PlayerActivity extends Activity
|
||||
if (extensions == null) {
|
||||
extensions = new String[uriStrings.length];
|
||||
}
|
||||
manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA);
|
||||
if (manifestFilters == null) {
|
||||
manifestFilters = new Parcelable[uriStrings.length];
|
||||
}
|
||||
} else {
|
||||
showToast(getString(R.string.unexpected_intent_action, action));
|
||||
finish();
|
||||
@ -413,9 +403,7 @@ public class PlayerActivity extends Activity
|
||||
|
||||
MediaSource[] mediaSources = new MediaSource[uris.length];
|
||||
for (int i = 0; i < uris.length; i++) {
|
||||
ParcelableArray<?> manifestFilter = (ParcelableArray<?>) manifestFilters[i];
|
||||
List<?> filter = manifestFilter != null ? manifestFilter.asList() : null;
|
||||
mediaSources[i] = buildMediaSource(uris[i], extensions[i], filter);
|
||||
mediaSources[i] = buildMediaSource(uris[i], extensions[i]);
|
||||
}
|
||||
mediaSource =
|
||||
mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
|
||||
@ -445,12 +433,11 @@ public class PlayerActivity extends Activity
|
||||
}
|
||||
|
||||
private MediaSource buildMediaSource(Uri uri) {
|
||||
return buildMediaSource(uri, null, null);
|
||||
return buildMediaSource(uri, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MediaSource buildMediaSource(
|
||||
Uri uri, @Nullable String overrideExtension, @Nullable List<?> manifestFilter) {
|
||||
private MediaSource buildMediaSource(Uri uri, @Nullable String overrideExtension) {
|
||||
@ContentType int type = Util.inferContentType(uri, overrideExtension);
|
||||
switch (type) {
|
||||
case C.TYPE_DASH:
|
||||
@ -459,7 +446,7 @@ public class PlayerActivity extends Activity
|
||||
buildDataSourceFactory(false))
|
||||
.setManifestParser(
|
||||
new FilteringManifestParser<>(
|
||||
new DashManifestParser(), (List<RepresentationKey>) manifestFilter))
|
||||
new DashManifestParser(), (List<RepresentationKey>) getOfflineStreamKeys(uri)))
|
||||
.createMediaSource(uri);
|
||||
case C.TYPE_SS:
|
||||
return new SsMediaSource.Factory(
|
||||
@ -467,13 +454,13 @@ public class PlayerActivity extends Activity
|
||||
buildDataSourceFactory(false))
|
||||
.setManifestParser(
|
||||
new FilteringManifestParser<>(
|
||||
new SsManifestParser(), (List<StreamKey>) manifestFilter))
|
||||
new SsManifestParser(), (List<StreamKey>) getOfflineStreamKeys(uri)))
|
||||
.createMediaSource(uri);
|
||||
case C.TYPE_HLS:
|
||||
return new HlsMediaSource.Factory(mediaDataSourceFactory)
|
||||
.setPlaylistParser(
|
||||
new FilteringManifestParser<>(
|
||||
new HlsPlaylistParser(), (List<RenditionKey>) manifestFilter))
|
||||
new HlsPlaylistParser(), (List<RenditionKey>) getOfflineStreamKeys(uri)))
|
||||
.createMediaSource(uri);
|
||||
case C.TYPE_OTHER:
|
||||
return new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri);
|
||||
@ -483,6 +470,10 @@ public class PlayerActivity extends Activity
|
||||
}
|
||||
}
|
||||
|
||||
private List<?> getOfflineStreamKeys(Uri uri) {
|
||||
return ((DemoApplication) getApplication()).getDownloadTracker().getOfflineStreamKeys(uri);
|
||||
}
|
||||
|
||||
private DefaultDrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManagerV18(
|
||||
UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, boolean multiSession)
|
||||
throws UnsupportedDrmException {
|
||||
|
@ -24,7 +24,6 @@ import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.util.JsonReader;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
@ -47,17 +46,27 @@ import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** An activity for selecting from a list of media samples. */
|
||||
public class SampleChooserActivity extends Activity {
|
||||
public class SampleChooserActivity extends Activity
|
||||
implements DownloadTracker.Listener, OnChildClickListener {
|
||||
|
||||
private static final String TAG = "SampleChooserActivity";
|
||||
|
||||
private DownloadTracker downloadTracker;
|
||||
private SampleAdapter sampleAdapter;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.sample_chooser_activity);
|
||||
sampleAdapter = new SampleAdapter();
|
||||
ExpandableListView sampleListView = findViewById(R.id.sample_list);
|
||||
sampleListView.setAdapter(sampleAdapter);
|
||||
sampleListView.setOnChildClickListener(this);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String dataUri = intent.getDataString();
|
||||
String[] uris;
|
||||
@ -80,10 +89,30 @@ public class SampleChooserActivity extends Activity {
|
||||
uriList.toArray(uris);
|
||||
Arrays.sort(uris);
|
||||
}
|
||||
|
||||
downloadTracker = ((DemoApplication) getApplication()).getDownloadTracker();
|
||||
startDownloadServiceForeground();
|
||||
|
||||
SampleListLoader loaderTask = new SampleListLoader();
|
||||
loaderTask.execute(uris);
|
||||
}
|
||||
|
||||
startDownloadServiceForeground();
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
downloadTracker.addListener(this);
|
||||
sampleAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
downloadTracker.removeListener(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadsChanged() {
|
||||
sampleAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void startDownloadServiceForeground() {
|
||||
@ -96,28 +125,44 @@ public class SampleChooserActivity extends Activity {
|
||||
Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
ExpandableListView sampleList = findViewById(R.id.sample_list);
|
||||
sampleList.setAdapter(new SampleAdapter(this, groups));
|
||||
sampleList.setOnChildClickListener(
|
||||
new OnChildClickListener() {
|
||||
@Override
|
||||
public boolean onChildClick(
|
||||
ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
|
||||
onSampleClicked(groups.get(groupPosition).samples.get(childPosition));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
sampleAdapter.setSampleGroups(groups);
|
||||
}
|
||||
|
||||
private void onSampleClicked(Sample sample) {
|
||||
@Override
|
||||
public boolean onChildClick(
|
||||
ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
|
||||
Sample sample = (Sample) view.getTag();
|
||||
startActivity(sample.buildIntent(this));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onSampleDownloadButtonClicked(Sample sample) {
|
||||
Intent intent = new Intent(this, DownloadActivity.class);
|
||||
intent.putExtra(DownloadActivity.SAMPLE_NAME, sample.name);
|
||||
intent.putExtra(DownloadActivity.PLAYER_INTENT, sample.buildIntent(this));
|
||||
startActivity(intent);
|
||||
int downloadUnsupportedStringId = getDownloadUnsupportedStringId(sample);
|
||||
if (downloadUnsupportedStringId != 0) {
|
||||
Toast.makeText(getApplicationContext(), downloadUnsupportedStringId, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
} else {
|
||||
UriSample uriSample = (UriSample) sample;
|
||||
downloadTracker.toggleDownload(this, sample.name, uriSample.uri, uriSample.extension);
|
||||
}
|
||||
}
|
||||
|
||||
private int getDownloadUnsupportedStringId(Sample sample) {
|
||||
if (sample instanceof PlaylistSample) {
|
||||
return R.string.download_playlist_unsupported;
|
||||
}
|
||||
UriSample uriSample = (UriSample) sample;
|
||||
if (uriSample.drmInfo != null) {
|
||||
return R.string.download_drm_unsupported;
|
||||
}
|
||||
if (uriSample.adTagUri != null) {
|
||||
return R.string.download_ads_unsupported;
|
||||
}
|
||||
String scheme = uriSample.uri.getScheme();
|
||||
if (!("http".equals(scheme) || "https".equals(scheme))) {
|
||||
return R.string.download_scheme_unsupported;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private final class SampleListLoader extends AsyncTask<String, Void, List<SampleGroup>> {
|
||||
@ -296,12 +341,15 @@ public class SampleChooserActivity extends Activity {
|
||||
|
||||
private final class SampleAdapter extends BaseExpandableListAdapter implements OnClickListener {
|
||||
|
||||
private final Context context;
|
||||
private final List<SampleGroup> sampleGroups;
|
||||
private List<SampleGroup> sampleGroups;
|
||||
|
||||
public SampleAdapter(Context context, List<SampleGroup> sampleGroups) {
|
||||
this.context = context;
|
||||
public SampleAdapter() {
|
||||
sampleGroups = Collections.emptyList();
|
||||
}
|
||||
|
||||
public void setSampleGroups(List<SampleGroup> sampleGroups) {
|
||||
this.sampleGroups = sampleGroups;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -319,7 +367,7 @@ public class SampleChooserActivity extends Activity {
|
||||
View convertView, ViewGroup parent) {
|
||||
View view = convertView;
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(R.layout.sample_list_item, parent, false);
|
||||
view = getLayoutInflater().inflate(R.layout.sample_list_item, parent, false);
|
||||
View downloadButton = view.findViewById(R.id.download_button);
|
||||
downloadButton.setOnClickListener(this);
|
||||
downloadButton.setFocusable(false);
|
||||
@ -348,8 +396,9 @@ public class SampleChooserActivity extends Activity {
|
||||
ViewGroup parent) {
|
||||
View view = convertView;
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(android.R.layout.simple_expandable_list_item_1,
|
||||
parent, false);
|
||||
view =
|
||||
getLayoutInflater()
|
||||
.inflate(android.R.layout.simple_expandable_list_item_1, parent, false);
|
||||
}
|
||||
((TextView) view).setText(getGroup(groupPosition).title);
|
||||
return view;
|
||||
@ -376,24 +425,18 @@ public class SampleChooserActivity extends Activity {
|
||||
}
|
||||
|
||||
private void initializeChildView(View view, Sample sample) {
|
||||
view.setTag(sample);
|
||||
TextView sampleTitle = view.findViewById(R.id.sample_title);
|
||||
sampleTitle.setText(sample.name);
|
||||
|
||||
boolean canDownload = getDownloadUnsupportedStringId(sample) == 0;
|
||||
boolean isDownloaded = canDownload && downloadTracker.isDownloaded(((UriSample) sample).uri);
|
||||
ImageButton downloadButton = view.findViewById(R.id.download_button);
|
||||
downloadButton.setTag(sample);
|
||||
downloadButton.setColorFilter(0xFFBBBBBB);
|
||||
downloadButton.setVisibility(canDownload(sample) ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private boolean canDownload(Sample sample) {
|
||||
if (!(sample instanceof UriSample)) {
|
||||
return false;
|
||||
}
|
||||
UriSample uriSample = (UriSample) sample;
|
||||
if (uriSample.drmInfo != null || uriSample.adTagUri != null) {
|
||||
return false;
|
||||
}
|
||||
String scheme = uriSample.uri.getScheme();
|
||||
return "http".equals(scheme) || "https".equals(scheme);
|
||||
downloadButton.setColorFilter(
|
||||
canDownload ? (isDownloaded ? 0xFF42A5F5 : 0xFFBDBDBD) : 0xFFEEEEEE);
|
||||
downloadButton.setImageResource(
|
||||
isDownloaded ? R.drawable.ic_download_done : R.drawable.ic_download);
|
||||
}
|
||||
}
|
||||
|
||||
|
BIN
demos/main/src/main/res/drawable-hdpi/ic_download.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
demos/main/src/main/res/drawable-hdpi/ic_download_done.png
Normal file
After Width: | Height: | Size: 218 B |
Before Width: | Height: | Size: 551 B |
BIN
demos/main/src/main/res/drawable-mdpi/ic_download.png
Normal file
After Width: | Height: | Size: 163 B |
BIN
demos/main/src/main/res/drawable-mdpi/ic_download_done.png
Normal file
After Width: | Height: | Size: 182 B |
Before Width: | Height: | Size: 388 B |
BIN
demos/main/src/main/res/drawable-xhdpi/ic_download.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
demos/main/src/main/res/drawable-xhdpi/ic_download_done.png
Normal file
After Width: | Height: | Size: 304 B |
Before Width: | Height: | Size: 691 B |
BIN
demos/main/src/main/res/drawable-xxhdpi/ic_download.png
Normal file
After Width: | Height: | Size: 303 B |
BIN
demos/main/src/main/res/drawable-xxhdpi/ic_download_done.png
Normal file
After Width: | Height: | Size: 450 B |
Before Width: | Height: | Size: 1.0 KiB |
BIN
demos/main/src/main/res/drawable-xxxhdpi/ic_download.png
Normal file
After Width: | Height: | Size: 304 B |
BIN
demos/main/src/main/res/drawable-xxxhdpi/ic_download_done.png
Normal file
After Width: | Height: | Size: 575 B |
Before Width: | Height: | Size: 1.3 KiB |
@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/activity_downloader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button android:id="@+id/download_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:text="@string/exo_download_description"/>
|
||||
|
||||
<Button android:id="@+id/remove_all_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:text="@string/download_remove_all"/>
|
||||
|
||||
<Button android:id="@+id/play_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:text="@string/exo_controls_play_description"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ListView android:id="@+id/representation_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
</LinearLayout>
|
@ -33,7 +33,6 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/exo_download_description"
|
||||
android:background="@android:color/transparent"
|
||||
android:src="@drawable/ic_offline_pin_white_36dp"/>
|
||||
android:background="@android:color/transparent"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
19
demos/main/src/main/res/layout/start_download_dialog.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2018 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.
|
||||
-->
|
||||
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/representation_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
@ -47,8 +47,14 @@
|
||||
|
||||
<string name="ima_not_loaded">Playing sample without ads, as the IMA extension was not loaded</string>
|
||||
|
||||
<string name="download_manifest_load_error">Failed to download manifest</string>
|
||||
<string name="download_start_error">Failed to start download</string>
|
||||
|
||||
<string name="download_remove_all">Remove all</string>
|
||||
<string name="download_playlist_unsupported">This demo app does not support downloading playlists</string>
|
||||
|
||||
<string name="download_drm_unsupported">This demo app does not support downloading protected content</string>
|
||||
|
||||
<string name="download_scheme_unsupported">This demo app only supports downloading http streams</string>
|
||||
|
||||
<string name="download_ads_unsupported">IMA does not support offline ads</string>
|
||||
|
||||
</resources>
|
||||
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** A {@link android.os.Parcelable} wrapper around an array. */
|
||||
public final class ParcelableArray<V extends Parcelable> implements Parcelable {
|
||||
|
||||
@SuppressWarnings("rawtypes") // V cannot be obtained from static context
|
||||
public static final Creator<ParcelableArray> CREATOR =
|
||||
new Creator<ParcelableArray>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ParcelableArray createFromParcel(Parcel in) {
|
||||
ClassLoader classLoader = ParcelableArray.class.getClassLoader();
|
||||
Parcelable[] elements = in.readParcelableArray(classLoader);
|
||||
return new ParcelableArray(elements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelableArray[] newArray(int size) {
|
||||
return new ParcelableArray[size];
|
||||
}
|
||||
};
|
||||
|
||||
private final V[] elements;
|
||||
|
||||
public ParcelableArray(V[] elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
|
||||
/** Returns an unmodifiable list containing all elements. */
|
||||
public List<V> asList() {
|
||||
return Collections.unmodifiableList(Arrays.asList(elements));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelableArray(elements, flags);
|
||||
}
|
||||
}
|
@ -15,15 +15,11 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.dash.manifest;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Uniquely identifies a {@link Representation} in a {@link DashManifest}.
|
||||
*/
|
||||
public final class RepresentationKey implements Parcelable, Comparable<RepresentationKey> {
|
||||
/** Uniquely identifies a {@link Representation} in a {@link DashManifest}. */
|
||||
public final class RepresentationKey implements Comparable<RepresentationKey> {
|
||||
|
||||
public final int periodIndex;
|
||||
public final int adaptationSetIndex;
|
||||
@ -77,31 +73,4 @@ public final class RepresentationKey implements Parcelable, Comparable<Represent
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(periodIndex);
|
||||
dest.writeInt(adaptationSetIndex);
|
||||
dest.writeInt(representationIndex);
|
||||
}
|
||||
|
||||
public static final Creator<RepresentationKey> CREATOR =
|
||||
new Creator<RepresentationKey>() {
|
||||
@Override
|
||||
public RepresentationKey createFromParcel(Parcel in) {
|
||||
return new RepresentationKey(in.readInt(), in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepresentationKey[] newArray(int size) {
|
||||
return new RepresentationKey[size];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.hls.playlist;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
@ -24,7 +22,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/** Uniquely identifies a rendition in an {@link HlsMasterPlaylist}. */
|
||||
public final class RenditionKey implements Parcelable, Comparable<RenditionKey> {
|
||||
public final class RenditionKey implements Comparable<RenditionKey> {
|
||||
|
||||
/** Types of rendition. */
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@ -78,30 +76,4 @@ public final class RenditionKey implements Parcelable, Comparable<RenditionKey>
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(type);
|
||||
dest.writeInt(trackIndex);
|
||||
}
|
||||
|
||||
public static final Creator<RenditionKey> CREATOR =
|
||||
new Creator<RenditionKey>() {
|
||||
@Override
|
||||
public RenditionKey createFromParcel(Parcel in) {
|
||||
return new RenditionKey(in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RenditionKey[] newArray(int size) {
|
||||
return new RenditionKey[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -15,13 +15,11 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.source.smoothstreaming.manifest;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
/** Uniquely identifies a track in a {@link SsManifest}. */
|
||||
public final class StreamKey implements Parcelable, Comparable<StreamKey> {
|
||||
public final class StreamKey implements Comparable<StreamKey> {
|
||||
|
||||
public final int streamElementIndex;
|
||||
public final int trackIndex;
|
||||
@ -66,30 +64,4 @@ public final class StreamKey implements Parcelable, Comparable<StreamKey> {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(streamElementIndex);
|
||||
dest.writeInt(trackIndex);
|
||||
}
|
||||
|
||||
public static final Creator<StreamKey> CREATOR =
|
||||
new Creator<StreamKey>() {
|
||||
@Override
|
||||
public StreamKey createFromParcel(Parcel in) {
|
||||
return new StreamKey(in.readInt(), in.readInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamKey[] newArray(int size) {
|
||||
return new StreamKey[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -23,13 +23,13 @@
|
||||
<string name="exo_track_selection_title_text">लेख</string>
|
||||
<string name="exo_track_selection_none">कोई नहीं</string>
|
||||
<string name="exo_track_selection_auto">अपने आप</string>
|
||||
<string name="exo_track_unknown">Unknown</string>
|
||||
<string name="exo_track_unknown">अज्ञात</string>
|
||||
<string name="exo_track_resolution">%1$d × %2$d</string>
|
||||
<string name="exo_track_mono">Mono</string>
|
||||
<string name="exo_track_stereo">Stereo</string>
|
||||
<string name="exo_track_surround">Surround sound</string>
|
||||
<string name="exo_track_surround_5_point_1">5.1 surround sound</string>
|
||||
<string name="exo_track_surround_7_point_1">7.1 surround sound</string>
|
||||
<string name="exo_track_bitrate">%1$.2f Mbps</string>
|
||||
<string name="exo_track_mono">मोनो साउंड</string>
|
||||
<string name="exo_track_stereo">स्टीरियो साउंड</string>
|
||||
<string name="exo_track_surround">सराउंड साउंड</string>
|
||||
<string name="exo_track_surround_5_point_1">5.1 सराउंड साउंड</string>
|
||||
<string name="exo_track_surround_7_point_1">7.1 सराउंड साउंड</string>
|
||||
<string name="exo_track_bitrate">%1$.2f एमबीपीएस</string>
|
||||
<string name="exo_item_list">%1$s, %2$s</string>
|
||||
</resources>
|
||||
|