Commit f4ade38a by eguven Committed by Oliver Woodman

Add ability to download not DRM protected content to the demo app

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=193554024
parent 117dd6f3
......@@ -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>
/*
* 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);
}
}
......@@ -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 {
......
<?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>
<?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>
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