mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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).
|
([#3792](https://github.com/google/ExoPlayer/issues/3792).
|
||||||
* Support 14-bit mode and little endianness in DTS PES packets
|
* Support 14-bit mode and little endianness in DTS PES packets
|
||||||
([#3340](https://github.com/google/ExoPlayer/issues/3340)).
|
([#3340](https://github.com/google/ExoPlayer/issues/3340)).
|
||||||
|
* Demo app: Add ability to download not DRM protected content.
|
||||||
|
|
||||||
### 2.6.1 ###
|
### 2.6.1 ###
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,9 @@
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<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.software.leanback" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
|
||||||
<uses-sdk/>
|
<uses-sdk/>
|
||||||
|
|
@ -73,6 +76,22 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</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>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</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.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
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.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
@ -84,6 +83,7 @@ import java.lang.reflect.Constructor;
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
import java.net.CookieManager;
|
import java.net.CookieManager;
|
||||||
import java.net.CookiePolicy;
|
import java.net.CookiePolicy;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -276,11 +276,15 @@ public class PlayerActivity extends Activity
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
Uri[] uris;
|
Uri[] uris;
|
||||||
String[] extensions;
|
String[] extensions;
|
||||||
Parcelable[] manifestFilters;
|
Object[] 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)};
|
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)) {
|
} 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];
|
||||||
|
|
@ -293,7 +297,7 @@ public class PlayerActivity extends Activity
|
||||||
}
|
}
|
||||||
manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA);
|
manifestFilters = intent.getParcelableArrayExtra(MANIFEST_FILTER_LIST_EXTRA);
|
||||||
if (manifestFilters == null) {
|
if (manifestFilters == null) {
|
||||||
manifestFilters = new Parcelable[uriStrings.length];
|
manifestFilters = new Object[uriStrings.length];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
showToast(getString(R.string.unexpected_intent_action, action));
|
showToast(getString(R.string.unexpected_intent_action, action));
|
||||||
|
|
@ -385,8 +389,15 @@ 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];
|
List<?> filter;
|
||||||
List<?> filter = manifestFilter != null ? manifestFilter.asList() : null;
|
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);
|
mediaSources[i] = buildMediaSource(uris[i], extensions[i], filter);
|
||||||
}
|
}
|
||||||
mediaSource =
|
mediaSource =
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,16 @@ import android.util.JsonReader;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.BaseExpandableListAdapter;
|
import android.widget.BaseExpandableListAdapter;
|
||||||
import android.widget.ExpandableListView;
|
import android.widget.ExpandableListView;
|
||||||
import android.widget.ExpandableListView.OnChildClickListener;
|
import android.widget.ExpandableListView.OnChildClickListener;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import com.google.android.exoplayer2.ParserException;
|
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.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSourceInputStream;
|
import com.google.android.exoplayer2.upstream.DataSourceInputStream;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
|
|
@ -81,6 +84,18 @@ public class SampleChooserActivity extends Activity {
|
||||||
}
|
}
|
||||||
SampleListLoader loaderTask = new SampleListLoader();
|
SampleListLoader loaderTask = new SampleListLoader();
|
||||||
loaderTask.execute(uris);
|
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) {
|
private void onSampleGroups(final List<SampleGroup> groups, boolean sawError) {
|
||||||
|
|
@ -104,6 +119,21 @@ public class SampleChooserActivity extends Activity {
|
||||||
startActivity(sample.buildIntent(this));
|
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 final class SampleListLoader extends AsyncTask<String, Void, List<SampleGroup>> {
|
||||||
|
|
||||||
private boolean sawError;
|
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 Context context;
|
||||||
private final List<SampleGroup> sampleGroups;
|
private final List<SampleGroup> sampleGroups;
|
||||||
|
private OnClickListener onClickListener;
|
||||||
|
|
||||||
public SampleAdapter(Context context, List<SampleGroup> sampleGroups) {
|
public SampleAdapter(Context context, List<SampleGroup> sampleGroups) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
@ -303,10 +334,9 @@ 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(android.R.layout.simple_list_item_1, parent,
|
view = LayoutInflater.from(context).inflate(R.layout.sample_list_item, parent, false);
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
((TextView) view).setText(getChild(groupPosition, childPosition).name);
|
initializeChildView(view, getChild(groupPosition, childPosition));
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,6 +382,28 @@ public class SampleChooserActivity extends Activity {
|
||||||
return true;
|
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 {
|
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="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>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue