diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java index c462c14c75..87d45148fe 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java @@ -25,6 +25,7 @@ import com.google.android.exoplayer2.offline.Download; import com.google.android.exoplayer2.offline.DownloadManager; import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.scheduler.PlatformScheduler; +import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.ui.DownloadNotificationHelper; import com.google.android.exoplayer2.util.NotificationUtil; import com.google.android.exoplayer2.util.Util; @@ -66,14 +67,16 @@ public class DemoDownloadService extends DownloadService { @Override @NonNull - protected Notification getForegroundNotification(@NonNull List downloads) { + protected Notification getForegroundNotification( + @NonNull List downloads, @Requirements.RequirementFlags int notMetRequirements) { return DemoUtil.getDownloadNotificationHelper(/* context= */ this) .buildProgressNotification( /* context= */ this, R.drawable.ic_download, /* contentIntent= */ null, /* message= */ null, - downloads); + downloads, + notMetRequirements); } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java index 527c51ea83..25ca27bdce 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java @@ -747,13 +747,15 @@ public abstract class DownloadService extends Service { * be implemented to throw {@link UnsupportedOperationException}. * * @param downloads The current downloads. + * @param notMetRequirements Any requirements for downloads that are not currently met. * @return The foreground notification to display. */ - protected abstract Notification getForegroundNotification(List downloads); + protected abstract Notification getForegroundNotification( + List downloads, @Requirements.RequirementFlags int notMetRequirements); /** * Invalidates the current foreground notification and causes {@link - * #getForegroundNotification(List)} to be invoked again if the service isn't stopped. + * #getForegroundNotification(List, int)} to be invoked again if the service isn't stopped. */ protected final void invalidateForegroundNotification() { if (foregroundNotificationUpdater != null && !isDestroyed) { @@ -908,7 +910,9 @@ public abstract class DownloadService extends Service { private void update() { List downloads = Assertions.checkNotNull(downloadManager).getCurrentDownloads(); - startForeground(notificationId, getForegroundNotification(downloads)); + @Requirements.RequirementFlags + int notMetRequirements = downloadManager.getNotMetRequirements(); + startForeground(notificationId, getForegroundNotification(downloads, notMetRequirements)); notificationDisplayed = true; if (periodicUpdatesStarted) { handler.removeCallbacksAndMessages(null); diff --git a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java index 98a7f6e887..e8a71798be 100644 --- a/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java +++ b/library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadServiceDashTest.java @@ -35,6 +35,7 @@ import com.google.android.exoplayer2.offline.DownloadRequest; import com.google.android.exoplayer2.offline.DownloadService; import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.robolectric.TestDownloadManagerListener; +import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.scheduler.Scheduler; import com.google.android.exoplayer2.testutil.DummyMainThread; import com.google.android.exoplayer2.testutil.FakeDataSet; @@ -139,7 +140,9 @@ public class DownloadServiceDashTest { } @Override - protected Notification getForegroundNotification(List downloads) { + protected Notification getForegroundNotification( + List downloads, + @Requirements.RequirementFlags int notMetRequirements) { throw new UnsupportedOperationException(); } }; diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationHelper.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationHelper.java index 83da4d54a8..fa7244b250 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationHelper.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationHelper.java @@ -24,6 +24,7 @@ import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.offline.Download; +import com.google.android.exoplayer2.scheduler.Requirements; import java.util.List; /** Helper for creating download notifications. */ @@ -42,6 +43,21 @@ public final class DownloadNotificationHelper { new NotificationCompat.Builder(context.getApplicationContext(), channelId); } + /** + * @deprecated Use {@link #buildProgressNotification(Context, int, PendingIntent, String, List, + * int)}. + */ + @Deprecated + public Notification buildProgressNotification( + Context context, + @DrawableRes int smallIcon, + @Nullable PendingIntent contentIntent, + @Nullable String message, + List downloads) { + return buildProgressNotification( + context, smallIcon, contentIntent, message, downloads, /* notMetRequirements= */ 0); + } + /** * Returns a progress notification for the given downloads. * @@ -50,6 +66,7 @@ public final class DownloadNotificationHelper { * @param contentIntent An optional content intent to send when the notification is clicked. * @param message An optional message to display on the notification. * @param downloads The downloads. + * @param notMetRequirements Any requirements for downloads that are not currently met. * @return The notification. */ public Notification buildProgressNotification( @@ -57,52 +74,88 @@ public final class DownloadNotificationHelper { @DrawableRes int smallIcon, @Nullable PendingIntent contentIntent, @Nullable String message, - List downloads) { + List downloads, + @Requirements.RequirementFlags int notMetRequirements) { float totalPercentage = 0; int downloadTaskCount = 0; boolean allDownloadPercentagesUnknown = true; boolean haveDownloadedBytes = false; - boolean haveDownloadTasks = false; - boolean haveRemoveTasks = false; + boolean haveDownloadingTasks = false; + boolean haveQueuedTasks = false; + boolean haveRemovingTasks = false; for (int i = 0; i < downloads.size(); i++) { Download download = downloads.get(i); - if (download.state == Download.STATE_REMOVING) { - haveRemoveTasks = true; - continue; + switch (download.state) { + case Download.STATE_REMOVING: + haveRemovingTasks = true; + break; + case Download.STATE_QUEUED: + haveQueuedTasks = true; + break; + case Download.STATE_RESTARTING: + case Download.STATE_DOWNLOADING: + haveDownloadingTasks = true; + float downloadPercentage = download.getPercentDownloaded(); + if (downloadPercentage != C.PERCENTAGE_UNSET) { + allDownloadPercentagesUnknown = false; + totalPercentage += downloadPercentage; + } + haveDownloadedBytes |= download.getBytesDownloaded() > 0; + downloadTaskCount++; + break; + // Terminal states aren't expected, but if we encounter them we do nothing. + case Download.STATE_STOPPED: + case Download.STATE_COMPLETED: + case Download.STATE_FAILED: + default: + break; } - if (download.state != Download.STATE_RESTARTING - && download.state != Download.STATE_DOWNLOADING) { - continue; - } - haveDownloadTasks = true; - float downloadPercentage = download.getPercentDownloaded(); - if (downloadPercentage != C.PERCENTAGE_UNSET) { - allDownloadPercentagesUnknown = false; - totalPercentage += downloadPercentage; - } - haveDownloadedBytes |= download.getBytesDownloaded() > 0; - downloadTaskCount++; } - int titleStringId = - haveDownloadTasks - ? R.string.exo_download_downloading - : (haveRemoveTasks ? R.string.exo_download_removing : NULL_STRING_ID); - int progress = 0; - boolean indeterminate = true; - if (haveDownloadTasks) { - progress = (int) (totalPercentage / downloadTaskCount); - indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes; + int titleStringId; + boolean showProgress = true; + if (haveDownloadingTasks) { + titleStringId = R.string.exo_download_downloading; + } else if (haveQueuedTasks && notMetRequirements != 0) { + showProgress = false; + if ((notMetRequirements & Requirements.NETWORK_UNMETERED) != 0) { + // Note: This assumes that "unmetered" == "WiFi", since it provides a clearer message that's + // correct in the majority of cases. + titleStringId = R.string.exo_download_paused_for_wifi; + } else if ((notMetRequirements & Requirements.NETWORK) != 0) { + titleStringId = R.string.exo_download_paused_for_network; + } else { + titleStringId = R.string.exo_download_paused; + } + } else if (haveRemovingTasks) { + titleStringId = R.string.exo_download_removing; + } else { + // There are either no downloads, or all downloads are in terminal states. + titleStringId = NULL_STRING_ID; } + + int maxProgress = 0; + int currentProgress = 0; + boolean indeterminateProgress = false; + if (showProgress) { + maxProgress = 100; + if (haveDownloadingTasks) { + currentProgress = (int) (totalPercentage / downloadTaskCount); + indeterminateProgress = allDownloadPercentagesUnknown && haveDownloadedBytes; + } else { + indeterminateProgress = true; + } + } + return buildNotification( context, smallIcon, contentIntent, message, titleStringId, - /* maxProgress= */ 100, - progress, - indeterminate, + maxProgress, + currentProgress, + indeterminateProgress, /* ongoing= */ true, /* showWhen= */ false); } diff --git a/library/ui/src/main/res/values/strings.xml b/library/ui/src/main/res/values/strings.xml index a11d04073f..0dde445c16 100644 --- a/library/ui/src/main/res/values/strings.xml +++ b/library/ui/src/main/res/values/strings.xml @@ -91,6 +91,12 @@ Download failed Removing downloads + + Downloads paused + + Downloads waiting for network + + Downloads waiting for WiFi Video