mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Add ability to download not DRM protected content to the demo app
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=193554024
This commit is contained in:
parent
117dd6f33f
commit
f4ade38ae4
9 changed files with 849 additions and 10 deletions
|
|
@ -246,6 +246,7 @@
|
|||
([#3792](https://github.com/google/ExoPlayer/issues/3792).
|
||||
* Support 14-bit mode and little endianness in DTS PES packets
|
||||
([#3340](https://github.com/google/ExoPlayer/issues/3340)).
|
||||
* Demo app: Add ability to download not DRM protected content.
|
||||
|
||||
### 2.6.1 ###
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
|
||||
<uses-feature android:name="android.software.leanback" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
|
||||
<uses-sdk/>
|
||||
|
|
@ -73,6 +76,22 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".DownloaderActivity"/>
|
||||
|
||||
<service
|
||||
android:exported="false"
|
||||
android:name=".DemoDownloadService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.android.exoplayer.downloadService.action.INIT"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<service
|
||||
android:name="com.google.android.exoplayer2.scheduler.PlatformScheduler$PlatformSchedulerService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="true"/>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.util.Pair;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager;
|
||||
import com.google.android.exoplayer2.offline.DownloadManager.DownloadState;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloadAction;
|
||||
import com.google.android.exoplayer2.scheduler.PlatformScheduler;
|
||||
import com.google.android.exoplayer2.scheduler.Requirements;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction;
|
||||
import com.google.android.exoplayer2.ui.DownloadNotificationUtil;
|
||||
import com.google.android.exoplayer2.ui.NotificationUtil;
|
||||
import com.google.android.exoplayer2.util.ErrorMessageProvider;
|
||||
import java.io.File;
|
||||
|
||||
/** Demo DownloadService implementation. */
|
||||
public class DemoDownloadService extends DownloadService {
|
||||
|
||||
private static final String CHANNEL_ID = "my_channel_01";
|
||||
|
||||
private static final int JOB_ID = 1;
|
||||
|
||||
private static final int FOREGROUND_NOTIFICATION_ID = 1;
|
||||
|
||||
private static DownloadManager downloadManager;
|
||||
|
||||
public DemoDownloadService() {
|
||||
super(FOREGROUND_NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
NotificationUtil.createNotificationChannel(
|
||||
this,
|
||||
CHANNEL_ID,
|
||||
R.string.download_notifications_channel_name,
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DownloadManager getDownloadManager() {
|
||||
if (downloadManager == null) {
|
||||
DemoApplication application = (DemoApplication) getApplication();
|
||||
DownloaderConstructorHelper constructorHelper =
|
||||
new DownloaderConstructorHelper(
|
||||
application.getDownloadCache(), application.buildHttpDataSourceFactory(null));
|
||||
String actionFilePath = new File(getExternalCacheDir(), "actionFile").getAbsolutePath();
|
||||
downloadManager =
|
||||
new DownloadManager(
|
||||
constructorHelper,
|
||||
/*maxSimultaneousDownloads=*/ 2,
|
||||
DownloadManager.DEFAULT_MIN_RETRY_COUNT,
|
||||
actionFilePath,
|
||||
DashDownloadAction.DESERIALIZER,
|
||||
HlsDownloadAction.DESERIALIZER,
|
||||
SsDownloadAction.DESERIALIZER,
|
||||
ProgressiveDownloadAction.DESERIALIZER);
|
||||
}
|
||||
return downloadManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlatformScheduler getScheduler() {
|
||||
return new PlatformScheduler(
|
||||
getApplicationContext(), getRequirements(), JOB_ID, ACTION_INIT, getPackageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Requirements getRequirements() {
|
||||
return new Requirements(Requirements.NETWORK_TYPE_UNMETERED, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Notification getForegroundNotification(DownloadState[] downloadStates) {
|
||||
return DownloadNotificationUtil.createProgressNotification(
|
||||
downloadStates, this, R.drawable.exo_controls_play, CHANNEL_ID, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStateChange(DownloadState downloadState) {
|
||||
int notificationId = FOREGROUND_NOTIFICATION_ID + 1 + downloadState.taskId;
|
||||
Notification downloadNotification =
|
||||
DownloadNotificationUtil.createDownloadFinishedNotification(
|
||||
downloadState,
|
||||
this,
|
||||
R.drawable.exo_controls_play,
|
||||
CHANNEL_ID,
|
||||
downloadState.downloadAction.getData(),
|
||||
new ErrorMessageProvider<Throwable>() {
|
||||
@Override
|
||||
public Pair<Integer, String> getErrorMessage(Throwable throwable) {
|
||||
return new Pair<>(0, throwable.getLocalizedMessage());
|
||||
}
|
||||
});
|
||||
NotificationUtil.setNotification(this, notificationId, downloadNotification);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* 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.DRM_SCHEME_EXTRA;
|
||||
import static com.google.android.exoplayer2.demo.PlayerActivity.EXTENSION_EXTRA;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.offline.DownloadAction;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloadAction;
|
||||
import com.google.android.exoplayer2.offline.ProgressiveDownloader;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.Representation;
|
||||
import com.google.android.exoplayer2.source.dash.manifest.RepresentationKey;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloadAction;
|
||||
import com.google.android.exoplayer2.source.dash.offline.DashDownloader;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction;
|
||||
import com.google.android.exoplayer2.source.hls.offline.HlsDownloader;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.manifest.TrackKey;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction;
|
||||
import com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloader;
|
||||
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 that downloads streams. */
|
||||
public class DownloaderActivity extends Activity {
|
||||
|
||||
public static final String PLAYER_INTENT = "player_intent";
|
||||
public static final String SAMPLE_NAME = "stream_name";
|
||||
|
||||
private static final String TAG = "DownloaderActivity";
|
||||
|
||||
private Intent playerIntent;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private AsyncTask manifestDownloaderTask;
|
||||
|
||||
private DownloadUtilMethods downloadUtilMethods;
|
||||
|
||||
private ListView representationList;
|
||||
private ArrayAdapter<RepresentationItem> arrayAdapter;
|
||||
private AlertDialog cancelDialog;
|
||||
private String sampleName;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.downloader_activity);
|
||||
|
||||
Intent intent = getIntent();
|
||||
playerIntent = intent.getParcelableExtra(PLAYER_INTENT);
|
||||
|
||||
TextView streamName = findViewById(R.id.sample_name);
|
||||
sampleName = intent.getStringExtra(SAMPLE_NAME);
|
||||
streamName.setText(sampleName);
|
||||
|
||||
arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_multiple_choice);
|
||||
representationList = findViewById(R.id.representation_list);
|
||||
representationList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
representationList.setAdapter(arrayAdapter);
|
||||
|
||||
if (playerIntent.hasExtra(DRM_SCHEME_EXTRA)) {
|
||||
showToastAndFinish(R.string.not_supported_content_type);
|
||||
}
|
||||
|
||||
Uri sampleUri = playerIntent.getData();
|
||||
DemoApplication application = (DemoApplication) getApplication();
|
||||
DownloaderConstructorHelper constructorHelper =
|
||||
new DownloaderConstructorHelper(
|
||||
application.getDownloadCache(), application.buildHttpDataSourceFactory(null));
|
||||
String extension = playerIntent.getStringExtra(EXTENSION_EXTRA);
|
||||
int type = Util.inferContentType(sampleUri, extension);
|
||||
switch (type) {
|
||||
case C.TYPE_DASH:
|
||||
downloadUtilMethods = new DashDownloadUtilMethods(sampleUri, constructorHelper);
|
||||
break;
|
||||
case C.TYPE_HLS:
|
||||
downloadUtilMethods = new HlsDownloadUtilMethods(sampleUri, constructorHelper);
|
||||
break;
|
||||
case C.TYPE_SS:
|
||||
downloadUtilMethods = new SsDownloadUtilMethods(sampleUri, constructorHelper);
|
||||
break;
|
||||
case C.TYPE_OTHER:
|
||||
downloadUtilMethods = new ProgressiveDownloadUtilMethods(sampleUri, constructorHelper);
|
||||
break;
|
||||
default:
|
||||
showToastAndFinish(R.string.not_supported_content_type);
|
||||
break;
|
||||
}
|
||||
|
||||
updateRepresentationsList();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
stopAll();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
updateRepresentationsList();
|
||||
}
|
||||
|
||||
// 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) {
|
||||
removeDownloaded();
|
||||
} else if (id == R.id.refresh) {
|
||||
updateRepresentationsList();
|
||||
} else if (id == R.id.play_button) {
|
||||
playDownloaded();
|
||||
}
|
||||
}
|
||||
|
||||
private void startDownload() {
|
||||
ArrayList<Object> representationKeys = getSelectedRepresentationKeys(true);
|
||||
if (representationKeys == null) {
|
||||
return;
|
||||
}
|
||||
DownloadService.addDownloadAction(
|
||||
this,
|
||||
DemoDownloadService.class,
|
||||
downloadUtilMethods.getDownloadAction(sampleName, representationKeys));
|
||||
}
|
||||
|
||||
private void removeDownloaded() {
|
||||
DownloadService.addDownloadAction(
|
||||
this, DemoDownloadService.class, downloadUtilMethods.getRemoveAction());
|
||||
showToastAndFinish(R.string.removing_all);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SuspiciousToArrayCall")
|
||||
private void playDownloaded() {
|
||||
ArrayList<Object> selectedRepresentationKeys = getSelectedRepresentationKeys(false);
|
||||
if (selectedRepresentationKeys.isEmpty()) {
|
||||
playerIntent.removeExtra(PlayerActivity.MANIFEST_FILTER_EXTRA);
|
||||
} else if (selectedRepresentationKeys.get(0) instanceof Parcelable) {
|
||||
Parcelable[] parcelables = new Parcelable[selectedRepresentationKeys.size()];
|
||||
selectedRepresentationKeys.toArray(parcelables);
|
||||
playerIntent.putExtra(
|
||||
PlayerActivity.MANIFEST_FILTER_EXTRA, new ParcelableArray<>(parcelables));
|
||||
} else {
|
||||
String[] strings = new String[selectedRepresentationKeys.size()];
|
||||
selectedRepresentationKeys.toArray(strings);
|
||||
playerIntent.putExtra(PlayerActivity.MANIFEST_FILTER_EXTRA, strings);
|
||||
}
|
||||
startActivity(playerIntent);
|
||||
}
|
||||
|
||||
private void stopAll() {
|
||||
if (cancelDialog != null) {
|
||||
cancelDialog.dismiss();
|
||||
}
|
||||
|
||||
if (manifestDownloaderTask != null) {
|
||||
manifestDownloaderTask.cancel(true);
|
||||
manifestDownloaderTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRepresentationsList() {
|
||||
if (cancelDialog == null) {
|
||||
cancelDialog =
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage("Please wait")
|
||||
.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
@Override
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
stopAll();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
cancelDialog.setTitle("Updating representations");
|
||||
cancelDialog.show();
|
||||
manifestDownloaderTask = new ManifestDownloaderTask().execute();
|
||||
}
|
||||
|
||||
private ArrayList<Object> getSelectedRepresentationKeys(boolean unselect) {
|
||||
SparseBooleanArray checked = representationList.getCheckedItemPositions();
|
||||
ArrayList<Object> representations = new ArrayList<>(checked.size());
|
||||
for (int i = 0; i < checked.size(); i++) {
|
||||
if (checked.valueAt(i)) {
|
||||
int position = checked.keyAt(i);
|
||||
RepresentationItem item =
|
||||
(RepresentationItem) representationList.getItemAtPosition(position);
|
||||
representations.add(item.key);
|
||||
if (unselect) {
|
||||
representationList.setItemChecked(position, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return representations;
|
||||
}
|
||||
|
||||
private void showToastAndFinish(int resId) {
|
||||
showToast(resId);
|
||||
finish();
|
||||
}
|
||||
|
||||
private void showToast(int resId) {
|
||||
Toast.makeText(getApplicationContext(), resId, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private static final class RepresentationItem {
|
||||
public final Object key;
|
||||
public final String title;
|
||||
|
||||
public RepresentationItem(Object key, String title) {
|
||||
this.key = key;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
||||
private final class ManifestDownloaderTask
|
||||
extends AsyncTask<Void, Void, List<RepresentationItem>> {
|
||||
|
||||
@Override
|
||||
protected List<RepresentationItem> doInBackground(Void... ignore) {
|
||||
List<RepresentationItem> items;
|
||||
try {
|
||||
items = downloadUtilMethods.getRepresentationItems();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
Log.e(TAG, "Getting representations failed", e);
|
||||
return null;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<RepresentationItem> items) {
|
||||
if (items == null) {
|
||||
showToastAndFinish(R.string.manifest_download_error);
|
||||
return;
|
||||
}
|
||||
arrayAdapter.clear();
|
||||
for (RepresentationItem representationItem : items) {
|
||||
arrayAdapter.add(representationItem);
|
||||
}
|
||||
stopAll();
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class DownloadUtilMethods {
|
||||
|
||||
protected final Uri manifestUri;
|
||||
protected final DownloaderConstructorHelper constructorHelper;
|
||||
|
||||
public DownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) {
|
||||
this.manifestUri = manifestUri;
|
||||
this.constructorHelper = constructorHelper;
|
||||
}
|
||||
|
||||
public abstract List<RepresentationItem> getRepresentationItems()
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
public abstract DownloadAction getDownloadAction(
|
||||
String sampleName, ArrayList<Object> representationKeys);
|
||||
|
||||
public abstract DownloadAction getRemoveAction();
|
||||
}
|
||||
|
||||
private static final class DashDownloadUtilMethods extends DownloadUtilMethods {
|
||||
|
||||
public DashDownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) {
|
||||
super(manifestUri, constructorHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RepresentationItem> getRepresentationItems()
|
||||
throws IOException, InterruptedException {
|
||||
DashDownloader downloader = new DashDownloader(manifestUri, constructorHelper);
|
||||
ArrayList<RepresentationItem> items = new ArrayList<>();
|
||||
for (RepresentationKey key : downloader.getAllRepresentationKeys()) {
|
||||
downloader.selectRepresentations(new RepresentationKey[] {key});
|
||||
try {
|
||||
downloader.init();
|
||||
} catch (IOException e) {
|
||||
continue;
|
||||
}
|
||||
Representation representation =
|
||||
downloader
|
||||
.getManifest()
|
||||
.getPeriod(key.periodIndex)
|
||||
.adaptationSets
|
||||
.get(key.adaptationSetIndex)
|
||||
.representations
|
||||
.get(key.representationIndex);
|
||||
String trackName = DemoUtil.buildTrackName(representation.format);
|
||||
int totalSegments = downloader.getTotalSegments();
|
||||
int downloadedRatio = 0;
|
||||
if (totalSegments != 0) {
|
||||
downloadedRatio = (downloader.getDownloadedSegments() * 100) / totalSegments;
|
||||
}
|
||||
String name = key.toString() + ' ' + trackName + ' ' + downloadedRatio + '%';
|
||||
items.add(new RepresentationItem(key, name));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
String sampleName, ArrayList<Object> representationKeys) {
|
||||
StringBuilder sb = new StringBuilder(sampleName);
|
||||
RepresentationKey[] keys =
|
||||
representationKeys.toArray(new RepresentationKey[representationKeys.size()]);
|
||||
for (RepresentationKey representationKey : keys) {
|
||||
sb.append('-').append(representationKey);
|
||||
}
|
||||
return new DashDownloadAction(manifestUri, false, sb.toString(), keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getRemoveAction() {
|
||||
return new DashDownloadAction(manifestUri, true, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class HlsDownloadUtilMethods extends DownloadUtilMethods {
|
||||
|
||||
public HlsDownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) {
|
||||
super(manifestUri, constructorHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RepresentationItem> getRepresentationItems()
|
||||
throws IOException, InterruptedException {
|
||||
HlsDownloader downloader = new HlsDownloader(manifestUri, constructorHelper);
|
||||
ArrayList<RepresentationItem> items = new ArrayList<>();
|
||||
for (String key : downloader.getAllRepresentationKeys()) {
|
||||
downloader.selectRepresentations(new String[] {key});
|
||||
try {
|
||||
downloader.init();
|
||||
} catch (IOException e) {
|
||||
continue;
|
||||
}
|
||||
int totalSegments = downloader.getTotalSegments();
|
||||
int downloadedRatio = 0;
|
||||
if (totalSegments != 0) {
|
||||
downloadedRatio = (downloader.getDownloadedSegments() * 100) / totalSegments;
|
||||
}
|
||||
String name = key + ' ' /*+ trackName + ' '*/ + downloadedRatio + '%';
|
||||
items.add(new RepresentationItem(key, name));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
String sampleName, ArrayList<Object> representationKeys) {
|
||||
StringBuilder sb = new StringBuilder(sampleName);
|
||||
String[] keys = representationKeys.toArray(new String[representationKeys.size()]);
|
||||
for (String key : keys) {
|
||||
sb.append('-').append(key);
|
||||
}
|
||||
return new HlsDownloadAction(manifestUri, false, sb.toString(), keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getRemoveAction() {
|
||||
return new HlsDownloadAction(manifestUri, true, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SsDownloadUtilMethods extends DownloadUtilMethods {
|
||||
|
||||
public SsDownloadUtilMethods(Uri manifestUri, DownloaderConstructorHelper constructorHelper) {
|
||||
super(manifestUri, constructorHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RepresentationItem> getRepresentationItems()
|
||||
throws IOException, InterruptedException {
|
||||
SsDownloader downloader = new SsDownloader(manifestUri, constructorHelper);
|
||||
ArrayList<RepresentationItem> items = new ArrayList<>();
|
||||
for (TrackKey key : downloader.getAllRepresentationKeys()) {
|
||||
downloader.selectRepresentations(new TrackKey[] {key});
|
||||
try {
|
||||
downloader.init();
|
||||
} catch (IOException e) {
|
||||
continue;
|
||||
}
|
||||
Format format =
|
||||
downloader.getManifest().streamElements[key.streamElementIndex].formats[key.trackIndex];
|
||||
String trackName = DemoUtil.buildTrackName(format);
|
||||
int totalSegments = downloader.getTotalSegments();
|
||||
int downloadedRatio = 0;
|
||||
if (totalSegments != 0) {
|
||||
downloadedRatio = (downloader.getDownloadedSegments() * 100) / totalSegments;
|
||||
}
|
||||
String name = key.toString() + ' ' + trackName + ' ' + downloadedRatio + '%';
|
||||
items.add(new RepresentationItem(key, name));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
String sampleName, ArrayList<Object> representationKeys) {
|
||||
StringBuilder sb = new StringBuilder(sampleName);
|
||||
TrackKey[] keys = representationKeys.toArray(new TrackKey[representationKeys.size()]);
|
||||
for (TrackKey trackKey : keys) {
|
||||
sb.append('-').append(trackKey);
|
||||
}
|
||||
return new SsDownloadAction(manifestUri, false, sb.toString(), keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getRemoveAction() {
|
||||
return new SsDownloadAction(manifestUri, true, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ProgressiveDownloadUtilMethods extends DownloadUtilMethods {
|
||||
|
||||
public ProgressiveDownloadUtilMethods(
|
||||
Uri manifestUri, DownloaderConstructorHelper constructorHelper) {
|
||||
super(manifestUri, constructorHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RepresentationItem> getRepresentationItems() {
|
||||
ProgressiveDownloader downloader =
|
||||
new ProgressiveDownloader(manifestUri.toString(), null, constructorHelper);
|
||||
ArrayList<RepresentationItem> items = new ArrayList<>();
|
||||
{
|
||||
downloader.init();
|
||||
int downloadedRatio = (int) downloader.getDownloadPercentage();
|
||||
String name = "track 1 - " + downloadedRatio + '%';
|
||||
items.add(new RepresentationItem(null, name));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getDownloadAction(
|
||||
String sampleName, ArrayList<Object> representationKeys) {
|
||||
return new ProgressiveDownloadAction(manifestUri.toString(), null, false, sampleName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DownloadAction getRemoveAction() {
|
||||
return new ProgressiveDownloadAction(manifestUri.toString(), null, true, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@ import android.content.pm.PackageManager;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.KeyEvent;
|
||||
|
|
@ -84,6 +83,7 @@ import java.lang.reflect.Constructor;
|
|||
import java.net.CookieHandler;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -276,11 +276,15 @@ public class PlayerActivity extends Activity
|
|||
String action = intent.getAction();
|
||||
Uri[] uris;
|
||||
String[] extensions;
|
||||
Parcelable[] manifestFilters;
|
||||
Object[] 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)};
|
||||
Object filter = intent.getParcelableExtra(MANIFEST_FILTER_EXTRA);
|
||||
if (filter == null) {
|
||||
filter = intent.getStringArrayExtra(MANIFEST_FILTER_EXTRA);
|
||||
}
|
||||
manifestFilters = new Object[] {filter};
|
||||
} else if (ACTION_VIEW_LIST.equals(action)) {
|
||||
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
|
||||
uris = new Uri[uriStrings.length];
|
||||
|
|
@ -293,7 +297,7 @@ public class PlayerActivity extends Activity
|
|||
}
|
||||
manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA);
|
||||
if (manifestFilters == null) {
|
||||
manifestFilters = new Parcelable[uriStrings.length];
|
||||
manifestFilters = new Object[uriStrings.length];
|
||||
}
|
||||
} else {
|
||||
showToast(getString(R.string.unexpected_intent_action, action));
|
||||
|
|
@ -385,8 +389,15 @@ 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;
|
||||
List<?> filter;
|
||||
Object manifestFilter = manifestFilters[i];
|
||||
if (manifestFilter instanceof ParcelableArray<?>) {
|
||||
filter = ((ParcelableArray<?>) manifestFilter).asList();
|
||||
} else if (manifestFilter instanceof String[]) {
|
||||
filter = Arrays.asList((String[]) manifestFilter);
|
||||
} else {
|
||||
filter = null;
|
||||
}
|
||||
mediaSources[i] = buildMediaSource(uris[i], extensions[i], filter);
|
||||
}
|
||||
mediaSource =
|
||||
|
|
|
|||
|
|
@ -26,13 +26,16 @@ 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;
|
||||
import android.widget.BaseExpandableListAdapter;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.ExpandableListView.OnChildClickListener;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import com.google.android.exoplayer2.ParserException;
|
||||
import com.google.android.exoplayer2.offline.DownloadService;
|
||||
import com.google.android.exoplayer2.upstream.DataSource;
|
||||
import com.google.android.exoplayer2.upstream.DataSourceInputStream;
|
||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||
|
|
@ -81,6 +84,18 @@ public class SampleChooserActivity extends Activity {
|
|||
}
|
||||
SampleListLoader loaderTask = new SampleListLoader();
|
||||
loaderTask.execute(uris);
|
||||
|
||||
startDownloadServiceForeground();
|
||||
}
|
||||
|
||||
private void startDownloadServiceForeground() {
|
||||
Intent serviceIntent =
|
||||
new Intent(DownloadService.ACTION_INIT).setPackage("com.google.android.exoplayer2.demo");
|
||||
if (Util.SDK_INT >= 26) {
|
||||
startForegroundService(serviceIntent);
|
||||
} else {
|
||||
startService(serviceIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private void onSampleGroups(final List<SampleGroup> groups, boolean sawError) {
|
||||
|
|
@ -104,6 +119,21 @@ public class SampleChooserActivity extends Activity {
|
|||
startActivity(sample.buildIntent(this));
|
||||
}
|
||||
|
||||
private void onSampleDownloadButtonClicked(Sample sample) {
|
||||
if (!(sample instanceof UriSample) || sample.drmInfo != null) {
|
||||
Toast.makeText(
|
||||
getApplicationContext(),
|
||||
R.string.supports_downloading_only_single_not_drm_protected,
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(this, DownloaderActivity.class);
|
||||
intent.putExtra(DownloaderActivity.SAMPLE_NAME, sample.name);
|
||||
intent.putExtra(DownloaderActivity.PLAYER_INTENT, sample.buildIntent(this));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private final class SampleListLoader extends AsyncTask<String, Void, List<SampleGroup>> {
|
||||
|
||||
private boolean sawError;
|
||||
|
|
@ -278,10 +308,11 @@ public class SampleChooserActivity extends Activity {
|
|||
|
||||
}
|
||||
|
||||
private static final class SampleAdapter extends BaseExpandableListAdapter {
|
||||
private final class SampleAdapter extends BaseExpandableListAdapter {
|
||||
|
||||
private final Context context;
|
||||
private final List<SampleGroup> sampleGroups;
|
||||
private OnClickListener onClickListener;
|
||||
|
||||
public SampleAdapter(Context context, List<SampleGroup> sampleGroups) {
|
||||
this.context = context;
|
||||
|
|
@ -303,10 +334,9 @@ public class SampleChooserActivity extends Activity {
|
|||
View convertView, ViewGroup parent) {
|
||||
View view = convertView;
|
||||
if (view == null) {
|
||||
view = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent,
|
||||
false);
|
||||
view = LayoutInflater.from(context).inflate(R.layout.sample_list_item, parent, false);
|
||||
}
|
||||
((TextView) view).setText(getChild(groupPosition, childPosition).name);
|
||||
initializeChildView(view, getChild(groupPosition, childPosition));
|
||||
return view;
|
||||
}
|
||||
|
||||
|
|
@ -352,6 +382,28 @@ public class SampleChooserActivity extends Activity {
|
|||
return true;
|
||||
}
|
||||
|
||||
private void initializeChildView(View view, Sample sample) {
|
||||
TextView sampleTitle = view.findViewById(R.id.sample_title);
|
||||
sampleTitle.setText(sample.name);
|
||||
|
||||
ImageButton downloadButton = view.findViewById(R.id.download_button);
|
||||
downloadButton.setFocusable(false);
|
||||
downloadButton.setOnClickListener(getOnClickListener());
|
||||
downloadButton.setTag(sample);
|
||||
}
|
||||
|
||||
private OnClickListener getOnClickListener() {
|
||||
if (onClickListener == null) {
|
||||
onClickListener =
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onSampleDownloadButtonClicked((Sample) v.getTag());
|
||||
}
|
||||
};
|
||||
}
|
||||
return onClickListener;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SampleGroup {
|
||||
|
|
|
|||
84
demos/main/src/main/res/layout/downloader_activity.xml
Normal file
84
demos/main/src/main/res/layout/downloader_activity.xml
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_downloader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="com.google.android.exoplayer2.demo.DownloaderActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/sample"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sample_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingLeft="5dp"
|
||||
tools:text="Sample Name"/>
|
||||
</LinearLayout>
|
||||
|
||||
<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/download"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/remove_all_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:text="@string/remove_all"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/refresh"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:text="@string/refresh"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/play_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:onClick="onClick"
|
||||
android:text="@string/play"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/representation_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</LinearLayout>
|
||||
43
demos/main/src/main/res/layout/sample_list_item.xml
Normal file
43
demos/main/src/main/res/layout/sample_list_item.xml
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dip"
|
||||
android:paddingEnd="12dip"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/sample_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeightSmall"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSmall"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/download_button"
|
||||
android:layout_width="35dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="Download the sample"
|
||||
android:gravity="center_vertical"
|
||||
android:src="@android:drawable/stat_sys_download"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -55,4 +55,24 @@
|
|||
|
||||
<string name="ima_not_loaded">Playing sample without ads, as the IMA extension was not loaded</string>
|
||||
|
||||
<string name="download_notifications_channel_name">Downloads</string>
|
||||
|
||||
<string name="sample">Sample:</string>
|
||||
|
||||
<string name="manifest_download_error">Manifest failed to download</string>
|
||||
|
||||
<string name="download">Download</string>
|
||||
|
||||
<string name="play">Play</string>
|
||||
|
||||
<string name="remove_all">Remove All</string>
|
||||
|
||||
<string name="removing_all">Removing all cached data.</string>
|
||||
|
||||
<string name="refresh">Refresh</string>
|
||||
|
||||
<string name="not_supported_content_type">Not supported content type.</string>
|
||||
|
||||
<string name="supports_downloading_only_single_not_drm_protected">Currently only downloading of single, not DRM protected content is demonstrated in this app.</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Reference in a new issue