diff --git a/app/src/main/java/com/cappielloantonio/play/service/DownloaderTracker.java b/app/src/main/java/com/cappielloantonio/play/service/DownloaderTracker.java index d05223c8..3111f686 100644 --- a/app/src/main/java/com/cappielloantonio/play/service/DownloaderTracker.java +++ b/app/src/main/java/com/cappielloantonio/play/service/DownloaderTracker.java @@ -1,29 +1,20 @@ package com.cappielloantonio.play.service; import static androidx.media3.common.util.Assertions.checkNotNull; -import static androidx.media3.common.util.Assertions.checkStateNotNull; import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; -import android.os.AsyncTask; import android.widget.Toast; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentManager; -import androidx.media3.common.DrmInitData; -import androidx.media3.common.Format; import androidx.media3.common.MediaItem; -import androidx.media3.common.TrackGroup; -import androidx.media3.common.TrackGroupArray; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; import androidx.media3.datasource.HttpDataSource; import androidx.media3.exoplayer.RenderersFactory; -import androidx.media3.exoplayer.drm.DrmSession; -import androidx.media3.exoplayer.drm.DrmSessionEventListener; -import androidx.media3.exoplayer.drm.OfflineLicenseHelper; import androidx.media3.exoplayer.offline.Download; import androidx.media3.exoplayer.offline.DownloadCursor; import androidx.media3.exoplayer.offline.DownloadHelper; @@ -39,10 +30,6 @@ import java.util.HashMap; import java.util.concurrent.CopyOnWriteArraySet; public class DownloaderTracker { - public interface Listener { - void onDownloadsChanged(); - } - private static final String TAG = "DownloadTracker"; private final Context context; @@ -55,6 +42,10 @@ public class DownloaderTracker { @Nullable private StartDownloadDialogHelper startDownloadDialogHelper; + public interface Listener { + void onDownloadsChanged(); + } + @SuppressLint("UnsafeOptInUsageError") public DownloaderTracker(Context context, HttpDataSource.Factory httpDataSourceFactory, DownloadManager downloadManager) { this.context = context.getApplicationContext(); @@ -141,9 +132,6 @@ public class DownloaderTracker { private TrackSelectionDialog trackSelectionDialog; private MappingTrackSelector.MappedTrackInfo mappedTrackInfo; - private WidevineOfflineLicenseFetchTask widevineOfflineLicenseFetchTask; - @Nullable - private byte[] keySetId; @SuppressLint("UnsafeOptInUsageError") public StartDownloadDialogHelper(FragmentManager fragmentManager, DownloadHelper downloadHelper, MediaItem mediaItem) { @@ -153,50 +141,25 @@ public class DownloaderTracker { downloadHelper.prepare(this); } + @SuppressLint("UnsafeOptInUsageError") public void release() { downloadHelper.release(); if (trackSelectionDialog != null) { trackSelectionDialog.dismiss(); } - if (widevineOfflineLicenseFetchTask != null) { - widevineOfflineLicenseFetchTask.cancel(false); - } } @SuppressLint("UnsafeOptInUsageError") @Override public void onPrepared(DownloadHelper helper) { - @Nullable Format format = getFirstFormatWithDrmInitData(helper); - if (format == null) { - onDownloadPrepared(helper); - return; - } - - // The content is DRM protected. We need to acquire an offline license. - if (Util.SDK_INT < 18) { - Toast.makeText(context, R.string.error_drm_unsupported_before_api_18, Toast.LENGTH_LONG).show(); - Log.e(TAG, "Downloading DRM protected content is not supported on API versions below 18"); - return; - } - - if (!hasSchemaData(format.drmInitData)) { - Toast.makeText(context, R.string.download_start_error_offline_license, Toast.LENGTH_LONG).show(); - Log.e(TAG, "Downloading content where DRM scheme data is not located in the manifest is not supported"); - return; - } - - widevineOfflineLicenseFetchTask = new WidevineOfflineLicenseFetchTask(format, mediaItem.localConfiguration.drmConfiguration, httpDataSourceFactory, this, helper); - widevineOfflineLicenseFetchTask.execute(); + onDownloadPrepared(helper); } @SuppressLint("UnsafeOptInUsageError") @Override public void onPrepareError(DownloadHelper helper, IOException e) { - boolean isLiveContent = e instanceof DownloadHelper.LiveContentUnsupportedException; - int toastStringId = isLiveContent ? R.string.download_live_unsupported : R.string.download_start_error; - String logMessage = isLiveContent ? "Downloading live content unsupported" : "Failed to start download"; - Toast.makeText(context, toastStringId, Toast.LENGTH_LONG).show(); - Log.e(TAG, logMessage, e); + Toast.makeText(context, R.string.download_start_error, Toast.LENGTH_LONG).show(); + Log.e(TAG, e.getMessage()); } // DialogInterface.OnClickListener implementation. @@ -216,14 +179,12 @@ public class DownloaderTracker { DownloadRequest downloadRequest = buildDownloadRequest(); if (downloadRequest.streamKeys.isEmpty()) { - // All tracks were deselected in the dialog. Don't start the download. return; } + startDownload(downloadRequest); } - // DialogInterface.OnDismissListener implementation. - @SuppressLint("UnsafeOptInUsageError") @Override public void onDismiss(DialogInterface dialogInterface) { @@ -231,46 +192,6 @@ public class DownloaderTracker { downloadHelper.release(); } - // Internal methods. - - /** - * Returns the first {@link Format} with a non-null {@link Format#drmInitData} found in the - * content's tracks, or null if none is found. - */ - @SuppressLint("UnsafeOptInUsageError") - @Nullable - private Format getFirstFormatWithDrmInitData(DownloadHelper helper) { - for (int periodIndex = 0; periodIndex < helper.getPeriodCount(); periodIndex++) { - MappingTrackSelector.MappedTrackInfo mappedTrackInfo = helper.getMappedTrackInfo(periodIndex); - for (int rendererIndex = 0; - rendererIndex < mappedTrackInfo.getRendererCount(); - rendererIndex++) { - TrackGroupArray trackGroups = mappedTrackInfo.getTrackGroups(rendererIndex); - for (int trackGroupIndex = 0; trackGroupIndex < trackGroups.length; trackGroupIndex++) { - TrackGroup trackGroup = trackGroups.get(trackGroupIndex); - for (int formatIndex = 0; formatIndex < trackGroup.length; formatIndex++) { - Format format = trackGroup.getFormat(formatIndex); - if (format.drmInitData != null) { - return format; - } - } - } - } - } - return null; - } - - private void onOfflineLicenseFetched(DownloadHelper helper, byte[] keySetId) { - this.keySetId = keySetId; - onDownloadPrepared(helper); - } - - private void onOfflineLicenseFetchedError(DrmSession.DrmSessionException e) { - Toast.makeText(context, R.string.download_start_error_offline_license, Toast.LENGTH_LONG) - .show(); - Log.e(TAG, "Failed to fetch offline DRM license", e); - } - @SuppressLint("UnsafeOptInUsageError") private void onDownloadPrepared(DownloadHelper helper) { if (helper.getPeriodCount() == 0) { @@ -280,7 +201,7 @@ public class DownloaderTracker { return; } - mappedTrackInfo = downloadHelper.getMappedTrackInfo(/* periodIndex= */ 0); + mappedTrackInfo = downloadHelper.getMappedTrackInfo(0); if (!TrackSelectionDialog.willHaveContent(mappedTrackInfo)) { Log.d(TAG, "No dialog content. Downloading entire stream."); @@ -294,20 +215,6 @@ public class DownloaderTracker { trackSelectionDialog.show(fragmentManager, null); } - /** - * Returns whether any the {@link DrmInitData.SchemeData} contained in {@code drmInitData} has - * non-null {@link DrmInitData.SchemeData#data}. - */ - @SuppressLint("UnsafeOptInUsageError") - private boolean hasSchemaData(DrmInitData drmInitData) { - for (int i = 0; i < drmInitData.schemeDataCount; i++) { - if (drmInitData.get(i).hasData()) { - return true; - } - } - return false; - } - private void startDownload() { startDownload(buildDownloadRequest()); } @@ -322,51 +229,4 @@ public class DownloaderTracker { return downloadHelper.getDownloadRequest(Util.getUtf8Bytes(checkNotNull(mediaItem.mediaMetadata.title.toString()))).copyWithKeySetId(keySetId); } } - - private static final class WidevineOfflineLicenseFetchTask extends AsyncTask { - private final Format format; - private final MediaItem.DrmConfiguration drmConfiguration; - private final HttpDataSource.Factory httpDataSourceFactory; - private final StartDownloadDialogHelper dialogHelper; - private final DownloadHelper downloadHelper; - - @Nullable - private byte[] keySetId; - @Nullable - private DrmSession.DrmSessionException drmSessionException; - - public WidevineOfflineLicenseFetchTask(Format format, MediaItem.DrmConfiguration drmConfiguration, HttpDataSource.Factory httpDataSourceFactory, StartDownloadDialogHelper dialogHelper, DownloadHelper downloadHelper) { - this.format = format; - this.drmConfiguration = drmConfiguration; - this.httpDataSourceFactory = httpDataSourceFactory; - this.dialogHelper = dialogHelper; - this.downloadHelper = downloadHelper; - } - - @SuppressLint("UnsafeOptInUsageError") - @Override - protected Void doInBackground(Void... voids) { - OfflineLicenseHelper offlineLicenseHelper = OfflineLicenseHelper.newWidevineInstance(drmConfiguration.licenseUri.toString(), drmConfiguration.forceDefaultLicenseUri, httpDataSourceFactory, drmConfiguration.licenseRequestHeaders, new DrmSessionEventListener.EventDispatcher()); - - try { - keySetId = offlineLicenseHelper.downloadLicense(format); - } catch (DrmSession.DrmSessionException e) { - drmSessionException = e; - } finally { - offlineLicenseHelper.release(); - } - - return null; - } - - @SuppressLint("UnsafeOptInUsageError") - @Override - protected void onPostExecute(Void aVoid) { - if (drmSessionException != null) { - dialogHelper.onOfflineLicenseFetchedError(drmSessionException); - } else { - dialogHelper.onOfflineLicenseFetched(downloadHelper, checkStateNotNull(keySetId)); - } - } - } }