Commit b9aaf1eb by olly Committed by Oliver Woodman

Improve offline support in the demo app

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=195593235
parent f2cef123
Showing with 154 additions and 478 deletions
...@@ -75,8 +75,6 @@ ...@@ -75,8 +75,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.google.android.exoplayer2.demo.DownloadActivity"/>
<service android:name="com.google.android.exoplayer2.demo.DemoDownloadService" <service android:name="com.google.android.exoplayer2.demo.DemoDownloadService"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
......
...@@ -43,6 +43,7 @@ import java.io.File; ...@@ -43,6 +43,7 @@ import java.io.File;
public class DemoApplication extends Application { public class DemoApplication extends Application {
private static final String DOWNLOAD_ACTION_FILE = "actions"; 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 String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
private static final int MAX_SIMULTANEOUS_DOWNLOADS = 2; private static final int MAX_SIMULTANEOUS_DOWNLOADS = 2;
private static final Deserializer[] DOWNLOAD_DESERIALIZERS = private static final Deserializer[] DOWNLOAD_DESERIALIZERS =
...@@ -58,6 +59,7 @@ public class DemoApplication extends Application { ...@@ -58,6 +59,7 @@ public class DemoApplication extends Application {
private File downloadDirectory; private File downloadDirectory;
private Cache downloadCache; private Cache downloadCache;
private DownloadManager downloadManager; private DownloadManager downloadManager;
private DownloadTracker downloadTracker;
@Override @Override
public void onCreate() { public void onCreate() {
...@@ -83,20 +85,36 @@ public class DemoApplication extends Application { ...@@ -83,20 +85,36 @@ public class DemoApplication extends Application {
return "withExtensions".equals(BuildConfig.FLAVOR); return "withExtensions".equals(BuildConfig.FLAVOR);
} }
/** Returns the download manager used by the application. */ public DownloadManager getDownloadManager() {
public synchronized DownloadManager getDownloadManager() { initDownloadManager();
return downloadManager;
}
public DownloadTracker getDownloadTracker() {
initDownloadManager();
return downloadTracker;
}
private synchronized void initDownloadManager() {
if (downloadManager == null) { if (downloadManager == null) {
DownloaderConstructorHelper constructorHelper = DownloaderConstructorHelper downloaderConstructorHelper =
new DownloaderConstructorHelper(getDownloadCache(), buildHttpDataSourceFactory(null)); new DownloaderConstructorHelper(
getDownloadCache(), buildHttpDataSourceFactory(/* listener= */ null));
downloadManager = downloadManager =
new DownloadManager( new DownloadManager(
constructorHelper, downloaderConstructorHelper,
MAX_SIMULTANEOUS_DOWNLOADS, MAX_SIMULTANEOUS_DOWNLOADS,
DownloadManager.DEFAULT_MIN_RETRY_COUNT, DownloadManager.DEFAULT_MIN_RETRY_COUNT,
new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE), new File(getDownloadDirectory(), DOWNLOAD_ACTION_FILE),
DOWNLOAD_DESERIALIZERS); 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() { private synchronized Cache getDownloadCache() {
......
/*
* 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;
}
}
...@@ -21,7 +21,6 @@ import android.content.Intent; ...@@ -21,7 +21,6 @@ import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.util.Pair; import android.util.Pair;
...@@ -83,7 +82,6 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; ...@@ -83,7 +82,6 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.util.ErrorMessageProvider; import com.google.android.exoplayer2.util.ErrorMessageProvider;
import com.google.android.exoplayer2.util.EventLogger; import com.google.android.exoplayer2.util.EventLogger;
import com.google.android.exoplayer2.util.ParcelableArray;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.net.CookieHandler; import java.net.CookieHandler;
...@@ -104,13 +102,11 @@ public class PlayerActivity extends Activity ...@@ -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 ACTION_VIEW = "com.google.android.exoplayer.demo.action.VIEW";
public static final String EXTENSION_EXTRA = "extension"; public static final String EXTENSION_EXTRA = "extension";
public static final String MANIFEST_FILTER_EXTRA = "manifest_filter";
public static final String ACTION_VIEW_LIST = public static final String ACTION_VIEW_LIST =
"com.google.android.exoplayer.demo.action.VIEW_LIST"; "com.google.android.exoplayer.demo.action.VIEW_LIST";
public static final String URI_LIST_EXTRA = "uri_list"; public static final String URI_LIST_EXTRA = "uri_list";
public static final String EXTENSION_LIST_EXTRA = "extension_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"; public static final String AD_TAG_URI_EXTRA = "ad_tag_uri";
...@@ -313,11 +309,9 @@ public class PlayerActivity extends Activity ...@@ -313,11 +309,9 @@ public class PlayerActivity extends Activity
String action = intent.getAction(); String action = intent.getAction();
Uri[] uris; Uri[] uris;
String[] extensions; String[] extensions;
Parcelable[] manifestFilters;
if (ACTION_VIEW.equals(action)) { if (ACTION_VIEW.equals(action)) {
uris = new Uri[] {intent.getData()}; uris = new Uri[] {intent.getData()};
extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)}; extensions = new String[] {intent.getStringExtra(EXTENSION_EXTRA)};
manifestFilters = new Parcelable[] {intent.getParcelableExtra(MANIFEST_FILTER_EXTRA)};
} else if (ACTION_VIEW_LIST.equals(action)) { } else if (ACTION_VIEW_LIST.equals(action)) {
String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA); String[] uriStrings = intent.getStringArrayExtra(URI_LIST_EXTRA);
uris = new Uri[uriStrings.length]; uris = new Uri[uriStrings.length];
...@@ -328,10 +322,6 @@ public class PlayerActivity extends Activity ...@@ -328,10 +322,6 @@ public class PlayerActivity extends Activity
if (extensions == null) { if (extensions == null) {
extensions = new String[uriStrings.length]; extensions = new String[uriStrings.length];
} }
manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA);
if (manifestFilters == null) {
manifestFilters = new Parcelable[uriStrings.length];
}
} else { } else {
showToast(getString(R.string.unexpected_intent_action, action)); showToast(getString(R.string.unexpected_intent_action, action));
finish(); finish();
...@@ -413,9 +403,7 @@ public class PlayerActivity extends Activity ...@@ -413,9 +403,7 @@ public class PlayerActivity extends Activity
MediaSource[] mediaSources = new MediaSource[uris.length]; MediaSource[] mediaSources = new MediaSource[uris.length];
for (int i = 0; i < uris.length; i++) { for (int i = 0; i < uris.length; i++) {
ParcelableArray<?> manifestFilter = (ParcelableArray<?>) manifestFilters[i]; mediaSources[i] = buildMediaSource(uris[i], extensions[i]);
List<?> filter = manifestFilter != null ? manifestFilter.asList() : null;
mediaSources[i] = buildMediaSource(uris[i], extensions[i], filter);
} }
mediaSource = mediaSource =
mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources);
...@@ -445,12 +433,11 @@ public class PlayerActivity extends Activity ...@@ -445,12 +433,11 @@ public class PlayerActivity extends Activity
} }
private MediaSource buildMediaSource(Uri uri) { private MediaSource buildMediaSource(Uri uri) {
return buildMediaSource(uri, null, null); return buildMediaSource(uri, null);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private MediaSource buildMediaSource( private MediaSource buildMediaSource(Uri uri, @Nullable String overrideExtension) {
Uri uri, @Nullable String overrideExtension, @Nullable List<?> manifestFilter) {
@ContentType int type = Util.inferContentType(uri, overrideExtension); @ContentType int type = Util.inferContentType(uri, overrideExtension);
switch (type) { switch (type) {
case C.TYPE_DASH: case C.TYPE_DASH:
...@@ -459,7 +446,7 @@ public class PlayerActivity extends Activity ...@@ -459,7 +446,7 @@ public class PlayerActivity extends Activity
buildDataSourceFactory(false)) buildDataSourceFactory(false))
.setManifestParser( .setManifestParser(
new FilteringManifestParser<>( new FilteringManifestParser<>(
new DashManifestParser(), (List<RepresentationKey>) manifestFilter)) new DashManifestParser(), (List<RepresentationKey>) getOfflineStreamKeys(uri)))
.createMediaSource(uri); .createMediaSource(uri);
case C.TYPE_SS: case C.TYPE_SS:
return new SsMediaSource.Factory( return new SsMediaSource.Factory(
...@@ -467,13 +454,13 @@ public class PlayerActivity extends Activity ...@@ -467,13 +454,13 @@ public class PlayerActivity extends Activity
buildDataSourceFactory(false)) buildDataSourceFactory(false))
.setManifestParser( .setManifestParser(
new FilteringManifestParser<>( new FilteringManifestParser<>(
new SsManifestParser(), (List<StreamKey>) manifestFilter)) new SsManifestParser(), (List<StreamKey>) getOfflineStreamKeys(uri)))
.createMediaSource(uri); .createMediaSource(uri);
case C.TYPE_HLS: case C.TYPE_HLS:
return new HlsMediaSource.Factory(mediaDataSourceFactory) return new HlsMediaSource.Factory(mediaDataSourceFactory)
.setPlaylistParser( .setPlaylistParser(
new FilteringManifestParser<>( new FilteringManifestParser<>(
new HlsPlaylistParser(), (List<RenditionKey>) manifestFilter)) new HlsPlaylistParser(), (List<RenditionKey>) getOfflineStreamKeys(uri)))
.createMediaSource(uri); .createMediaSource(uri);
case C.TYPE_OTHER: case C.TYPE_OTHER:
return new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri); return new ExtractorMediaSource.Factory(mediaDataSourceFactory).createMediaSource(uri);
...@@ -483,6 +470,10 @@ public class PlayerActivity extends Activity ...@@ -483,6 +470,10 @@ public class PlayerActivity extends Activity
} }
} }
private List<?> getOfflineStreamKeys(Uri uri) {
return ((DemoApplication) getApplication()).getDownloadTracker().getOfflineStreamKeys(uri);
}
private DefaultDrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManagerV18( private DefaultDrmSessionManager<FrameworkMediaCrypto> buildDrmSessionManagerV18(
UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, boolean multiSession) UUID uuid, String licenseUrl, String[] keyRequestPropertiesArray, boolean multiSession)
throws UnsupportedDrmException { throws UnsupportedDrmException {
......
...@@ -24,7 +24,6 @@ import android.os.AsyncTask; ...@@ -24,7 +24,6 @@ import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.util.JsonReader; import android.util.JsonReader;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -47,17 +46,27 @@ import java.io.InputStream; ...@@ -47,17 +46,27 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
/** An activity for selecting from a list of media samples. */ /** 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 static final String TAG = "SampleChooserActivity";
private DownloadTracker downloadTracker;
private SampleAdapter sampleAdapter;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.sample_chooser_activity); 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(); Intent intent = getIntent();
String dataUri = intent.getDataString(); String dataUri = intent.getDataString();
String[] uris; String[] uris;
...@@ -80,10 +89,30 @@ public class SampleChooserActivity extends Activity { ...@@ -80,10 +89,30 @@ public class SampleChooserActivity extends Activity {
uriList.toArray(uris); uriList.toArray(uris);
Arrays.sort(uris); Arrays.sort(uris);
} }
downloadTracker = ((DemoApplication) getApplication()).getDownloadTracker();
startDownloadServiceForeground();
SampleListLoader loaderTask = new SampleListLoader(); SampleListLoader loaderTask = new SampleListLoader();
loaderTask.execute(uris); 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() { private void startDownloadServiceForeground() {
...@@ -96,28 +125,44 @@ public class SampleChooserActivity extends Activity { ...@@ -96,28 +125,44 @@ public class SampleChooserActivity extends Activity {
Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG) Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG)
.show(); .show();
} }
ExpandableListView sampleList = findViewById(R.id.sample_list); sampleAdapter.setSampleGroups(groups);
sampleList.setAdapter(new SampleAdapter(this, groups)); }
sampleList.setOnChildClickListener(
new OnChildClickListener() {
@Override @Override
public boolean onChildClick( public boolean onChildClick(
ExpandableListView parent, View view, int groupPosition, int childPosition, long id) { ExpandableListView parent, View view, int groupPosition, int childPosition, long id) {
onSampleClicked(groups.get(groupPosition).samples.get(childPosition)); Sample sample = (Sample) view.getTag();
startActivity(sample.buildIntent(this));
return true; return true;
} }
});
}
private void onSampleClicked(Sample sample) { private void onSampleDownloadButtonClicked(Sample sample) {
startActivity(sample.buildIntent(this)); 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 void onSampleDownloadButtonClicked(Sample sample) { private int getDownloadUnsupportedStringId(Sample sample) {
Intent intent = new Intent(this, DownloadActivity.class); if (sample instanceof PlaylistSample) {
intent.putExtra(DownloadActivity.SAMPLE_NAME, sample.name); return R.string.download_playlist_unsupported;
intent.putExtra(DownloadActivity.PLAYER_INTENT, sample.buildIntent(this)); }
startActivity(intent); 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>> { private final class SampleListLoader extends AsyncTask<String, Void, List<SampleGroup>> {
...@@ -296,12 +341,15 @@ public class SampleChooserActivity extends Activity { ...@@ -296,12 +341,15 @@ public class SampleChooserActivity extends Activity {
private final class SampleAdapter extends BaseExpandableListAdapter implements OnClickListener { private final class SampleAdapter extends BaseExpandableListAdapter implements OnClickListener {
private final Context context; private List<SampleGroup> sampleGroups;
private final List<SampleGroup> sampleGroups;
public SampleAdapter(Context context, List<SampleGroup> sampleGroups) { public SampleAdapter() {
this.context = context; sampleGroups = Collections.emptyList();
}
public void setSampleGroups(List<SampleGroup> sampleGroups) {
this.sampleGroups = sampleGroups; this.sampleGroups = sampleGroups;
notifyDataSetChanged();
} }
@Override @Override
...@@ -319,7 +367,7 @@ public class SampleChooserActivity extends Activity { ...@@ -319,7 +367,7 @@ public class SampleChooserActivity extends Activity {
View convertView, ViewGroup parent) { View convertView, ViewGroup parent) {
View view = convertView; View view = convertView;
if (view == null) { 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); View downloadButton = view.findViewById(R.id.download_button);
downloadButton.setOnClickListener(this); downloadButton.setOnClickListener(this);
downloadButton.setFocusable(false); downloadButton.setFocusable(false);
...@@ -348,8 +396,9 @@ public class SampleChooserActivity extends Activity { ...@@ -348,8 +396,9 @@ public class SampleChooserActivity extends Activity {
ViewGroup parent) { ViewGroup parent) {
View view = convertView; View view = convertView;
if (view == null) { if (view == null) {
view = LayoutInflater.from(context).inflate(android.R.layout.simple_expandable_list_item_1, view =
parent, false); getLayoutInflater()
.inflate(android.R.layout.simple_expandable_list_item_1, parent, false);
} }
((TextView) view).setText(getGroup(groupPosition).title); ((TextView) view).setText(getGroup(groupPosition).title);
return view; return view;
...@@ -376,24 +425,18 @@ public class SampleChooserActivity extends Activity { ...@@ -376,24 +425,18 @@ public class SampleChooserActivity extends Activity {
} }
private void initializeChildView(View view, Sample sample) { private void initializeChildView(View view, Sample sample) {
view.setTag(sample);
TextView sampleTitle = view.findViewById(R.id.sample_title); TextView sampleTitle = view.findViewById(R.id.sample_title);
sampleTitle.setText(sample.name); 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); ImageButton downloadButton = view.findViewById(R.id.download_button);
downloadButton.setTag(sample); downloadButton.setTag(sample);
downloadButton.setColorFilter(0xFFBBBBBB); downloadButton.setColorFilter(
downloadButton.setVisibility(canDownload(sample) ? View.VISIBLE : View.GONE); canDownload ? (isDownloaded ? 0xFF42A5F5 : 0xFFBDBDBD) : 0xFFEEEEEE);
} downloadButton.setImageResource(
isDownloaded ? R.drawable.ic_download_done : R.drawable.ic_download);
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);
} }
} }
......
<?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 @@ ...@@ -33,7 +33,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:contentDescription="@string/exo_download_description" android:contentDescription="@string/exo_download_description"
android:background="@android:color/transparent" android:background="@android:color/transparent"/>
android:src="@drawable/ic_offline_pin_white_36dp"/>
</LinearLayout> </LinearLayout>
<?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 @@ ...@@ -47,8 +47,14 @@
<string name="ima_not_loaded">Playing sample without ads, as the IMA extension was not loaded</string> <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> </resources>
/*
* 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 @@ ...@@ -15,15 +15,11 @@
*/ */
package com.google.android.exoplayer2.source.dash.manifest; 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.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
/** /** Uniquely identifies a {@link Representation} in a {@link DashManifest}. */
* Uniquely identifies a {@link Representation} in a {@link DashManifest}. public final class RepresentationKey implements Comparable<RepresentationKey> {
*/
public final class RepresentationKey implements Parcelable, Comparable<RepresentationKey> {
public final int periodIndex; public final int periodIndex;
public final int adaptationSetIndex; public final int adaptationSetIndex;
...@@ -77,31 +73,4 @@ public final class RepresentationKey implements Parcelable, Comparable<Represent ...@@ -77,31 +73,4 @@ public final class RepresentationKey implements Parcelable, Comparable<Represent
return result; return result;
} }
// Parcelable implementation.
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(periodIndex);
dest.writeInt(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 @@ ...@@ -15,8 +15,6 @@
*/ */
package com.google.android.exoplayer2.source.hls.playlist; 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.IntDef;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
...@@ -24,7 +22,7 @@ import java.lang.annotation.Retention; ...@@ -24,7 +22,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
/** Uniquely identifies a rendition in an {@link HlsMasterPlaylist}. */ /** 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. */ /** Types of rendition. */
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
...@@ -78,30 +76,4 @@ public final class RenditionKey implements Parcelable, Comparable<RenditionKey> ...@@ -78,30 +76,4 @@ public final class RenditionKey implements Parcelable, Comparable<RenditionKey>
} }
return result; 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 @@ ...@@ -15,13 +15,11 @@
*/ */
package com.google.android.exoplayer2.source.smoothstreaming.manifest; 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.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
/** Uniquely identifies a track in a {@link SsManifest}. */ /** 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 streamElementIndex;
public final int trackIndex; public final int trackIndex;
...@@ -66,30 +64,4 @@ public final class StreamKey implements Parcelable, Comparable<StreamKey> { ...@@ -66,30 +64,4 @@ public final class StreamKey implements Parcelable, Comparable<StreamKey> {
} }
return result; 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 @@ ...@@ -23,13 +23,13 @@
<string name="exo_track_selection_title_text">लेख</string> <string name="exo_track_selection_title_text">लेख</string>
<string name="exo_track_selection_none">कोई नहीं</string> <string name="exo_track_selection_none">कोई नहीं</string>
<string name="exo_track_selection_auto">अपने आप</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_resolution">%1$d × %2$d</string>
<string name="exo_track_mono">Mono</string> <string name="exo_track_mono">मोनो साउंड</string>
<string name="exo_track_stereo">Stereo</string> <string name="exo_track_stereo">स्टीरियो साउंड</string>
<string name="exo_track_surround">Surround sound</string> <string name="exo_track_surround">सराउंड साउंड</string>
<string name="exo_track_surround_5_point_1">5.1 surround sound</string> <string name="exo_track_surround_5_point_1">5.1 सराउंड साउंड</string>
<string name="exo_track_surround_7_point_1">7.1 surround sound</string> <string name="exo_track_surround_7_point_1">7.1 सराउंड साउंड</string>
<string name="exo_track_bitrate">%1$.2f Mbps</string> <string name="exo_track_bitrate">%1$.2f एमबीपीएस</string>
<string name="exo_item_list">%1$s, %2$s</string> <string name="exo_item_list">%1$s, %2$s</string>
</resources> </resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment