From 9594aa45ff316a24d9cab2859fd3bd950e4bfdaa Mon Sep 17 00:00:00 2001 From: andrewlewis Date: Mon, 20 Jul 2020 17:47:29 +0100 Subject: [PATCH] Move functionality from DemoApplication to DemoUtil https://developer.android.com/reference/android/app/Application recommends against subclassing application. PiperOrigin-RevId: 322163812 --- demos/main/build.gradle | 1 + demos/main/src/main/AndroidManifest.xml | 2 +- .../exoplayer2/demo/DemoDownloadService.java | 18 ++- .../{DemoApplication.java => DemoUtil.java} | 142 ++++++++++-------- .../exoplayer2/demo/PlayerActivity.java | 6 +- .../demo/SampleChooserActivity.java | 9 +- .../ui/DownloadNotificationHelper.java | 28 +++- .../ui/DownloadNotificationUtil.java | 6 +- 8 files changed, 123 insertions(+), 89 deletions(-) rename demos/main/src/main/java/com/google/android/exoplayer2/demo/{DemoApplication.java => DemoUtil.java} (51%) diff --git a/demos/main/build.gradle b/demos/main/build.gradle index abf5a471b8..e2e485b76a 100644 --- a/demos/main/build.gradle +++ b/demos/main/build.gradle @@ -63,6 +63,7 @@ android { } dependencies { + compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion implementation 'androidx.multidex:multidex:' + androidxMultidexVersion diff --git a/demos/main/src/main/AndroidManifest.xml b/demos/main/src/main/AndroidManifest.xml index 0240a377ac..d0bf623749 100644 --- a/demos/main/src/main/AndroidManifest.xml +++ b/demos/main/src/main/AndroidManifest.xml @@ -35,7 +35,7 @@ android:largeHeap="true" android:allowBackup="false" android:requestLegacyExternalStorage="true" - android:name="com.google.android.exoplayer2.demo.DemoApplication" + android:name="androidx.multidex.MultiDexApplication" tools:ignore="UnusedAttribute"> downloads) { - return ((DemoApplication) getApplication()) - .getDownloadNotificationHelper() + return DemoUtil.getDownloadNotificationHelper(/* context= */ this) .buildProgressNotification( - R.drawable.ic_download, /* contentIntent= */ null, /* message= */ null, downloads); + /* context= */ this, + R.drawable.ic_download, + /* contentIntent= */ null, + /* message= */ null, + downloads); } /** @@ -101,12 +103,14 @@ public class DemoDownloadService extends DownloadService { if (download.state == Download.STATE_COMPLETED) { notification = notificationHelper.buildDownloadCompletedNotification( + context, R.drawable.ic_download_done, /* contentIntent= */ null, Util.fromUtf8Bytes(download.request.data)); } else if (download.state == Download.STATE_FAILED) { notification = notificationHelper.buildDownloadFailedNotification( + context, R.drawable.ic_download_done, /* contentIntent= */ null, Util.fromUtf8Bytes(download.request.data)); diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoUtil.java similarity index 51% rename from demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java rename to demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoUtil.java index f4205efbb4..ed7a71b156 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoApplication.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoUtil.java @@ -15,7 +15,7 @@ */ package com.google.android.exoplayer2.demo; -import androidx.multidex.MultiDexApplication; +import android.content.Context; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.RenderersFactory; import com.google.android.exoplayer2.database.DatabaseProvider; @@ -38,53 +38,34 @@ import com.google.android.exoplayer2.util.Util; import java.io.File; import java.io.IOException; import java.util.concurrent.Executors; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; -/** - * Placeholder application to facilitate overriding Application methods for debugging and testing. - */ -public class DemoApplication extends MultiDexApplication { +/** Utility methods for the demo app. */ +public final class DemoUtil { public static final String DOWNLOAD_NOTIFICATION_CHANNEL_ID = "download_channel"; - private static final String TAG = "DemoApplication"; + private static final String TAG = "DemoUtil"; 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 HttpDataSource.Factory httpDataSourceFactory; - private DatabaseProvider databaseProvider; - private File downloadDirectory; - private Cache downloadCache; - private DownloadManager downloadManager; - private DownloadTracker downloadTracker; - private DownloadNotificationHelper downloadNotificationHelper; - - @Override - public void onCreate() { - super.onCreate(); - CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper(/* context= */ this); - String userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); - httpDataSourceFactory = - new CronetDataSourceFactory( - cronetEngineWrapper, - Executors.newSingleThreadExecutor(), - /* transferListener= */ null, - userAgent); - } - - /** Returns a {@link DataSource.Factory}. */ - public DataSource.Factory buildDataSourceFactory() { - DefaultDataSourceFactory upstreamFactory = - new DefaultDataSourceFactory(/* context= */ this, httpDataSourceFactory); - return buildReadOnlyCacheDataSource(upstreamFactory, getDownloadCache()); - } + private static DataSource.@MonotonicNonNull Factory dataSourceFactory; + private static HttpDataSource.@MonotonicNonNull Factory httpDataSourceFactory; + private static @MonotonicNonNull DatabaseProvider databaseProvider; + private static @MonotonicNonNull File downloadDirectory; + private static @MonotonicNonNull Cache downloadCache; + private static @MonotonicNonNull DownloadManager downloadManager; + private static @MonotonicNonNull DownloadTracker downloadTracker; + private static @MonotonicNonNull DownloadNotificationHelper downloadNotificationHelper; /** Returns whether extension renderers should be used. */ - public boolean useExtensionRenderers() { + public static boolean useExtensionRenderers() { return "withDecoderExtensions".equals(BuildConfig.FLAVOR); } - public RenderersFactory buildRenderersFactory(boolean preferExtensionRenderer) { + public static RenderersFactory buildRenderersFactory( + Context context, boolean preferExtensionRenderer) { @DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = useExtensionRenderers() @@ -92,61 +73,96 @@ public class DemoApplication extends MultiDexApplication { ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON) : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF; - return new DefaultRenderersFactory(/* context= */ this) + return new DefaultRenderersFactory(context.getApplicationContext()) .setExtensionRendererMode(extensionRendererMode); } - public DownloadNotificationHelper getDownloadNotificationHelper() { + public static synchronized HttpDataSource.Factory getHttpDataSourceFactory(Context context) { + if (httpDataSourceFactory == null) { + context = context.getApplicationContext(); + CronetEngineWrapper cronetEngineWrapper = new CronetEngineWrapper(context); + String userAgent = Util.getUserAgent(context, "ExoPlayerDemo"); + httpDataSourceFactory = + new CronetDataSourceFactory( + cronetEngineWrapper, + Executors.newSingleThreadExecutor(), + /* transferListener= */ null, + userAgent); + } + return httpDataSourceFactory; + } + + /** Returns a {@link DataSource.Factory}. */ + public static synchronized DataSource.Factory buildDataSourceFactory(Context context) { + if (dataSourceFactory == null) { + context = context.getApplicationContext(); + DefaultDataSourceFactory upstreamFactory = + new DefaultDataSourceFactory(context, getHttpDataSourceFactory(context)); + dataSourceFactory = buildReadOnlyCacheDataSource(upstreamFactory, getDownloadCache(context)); + } + return dataSourceFactory; + } + + public static synchronized DownloadNotificationHelper getDownloadNotificationHelper( + Context context) { if (downloadNotificationHelper == null) { downloadNotificationHelper = - new DownloadNotificationHelper(this, DOWNLOAD_NOTIFICATION_CHANNEL_ID); + new DownloadNotificationHelper(context, DOWNLOAD_NOTIFICATION_CHANNEL_ID); } return downloadNotificationHelper; } - public DownloadManager getDownloadManager() { - initDownloadManager(); + public static synchronized DownloadManager getDownloadManager(Context context) { + ensureDownloadManagerInitialized(context); return downloadManager; } - public DownloadTracker getDownloadTracker() { - initDownloadManager(); + public static synchronized DownloadTracker getDownloadTracker(Context context) { + ensureDownloadManagerInitialized(context); return downloadTracker; } - private synchronized Cache getDownloadCache() { + private static synchronized Cache getDownloadCache(Context context) { if (downloadCache == null) { - File downloadContentDirectory = new File(getDownloadDirectory(), DOWNLOAD_CONTENT_DIRECTORY); + File downloadContentDirectory = + new File(getDownloadDirectory(context), DOWNLOAD_CONTENT_DIRECTORY); downloadCache = - new SimpleCache(downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider()); + new SimpleCache( + downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider(context)); } return downloadCache; } - private synchronized void initDownloadManager() { + private static synchronized void ensureDownloadManagerInitialized(Context context) { if (downloadManager == null) { - DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider()); + DefaultDownloadIndex downloadIndex = new DefaultDownloadIndex(getDatabaseProvider(context)); upgradeActionFile( - DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false); + context, DOWNLOAD_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ false); upgradeActionFile( - DOWNLOAD_TRACKER_ACTION_FILE, downloadIndex, /* addNewDownloadsAsCompleted= */ true); + context, + DOWNLOAD_TRACKER_ACTION_FILE, + downloadIndex, + /* addNewDownloadsAsCompleted= */ true); downloadManager = new DownloadManager( - /* context= */ this, - getDatabaseProvider(), - getDownloadCache(), - httpDataSourceFactory, + context, + getDatabaseProvider(context), + getDownloadCache(context), + getHttpDataSourceFactory(context), Executors.newFixedThreadPool(/* nThreads= */ 6)); downloadTracker = - new DownloadTracker(/* context= */ this, buildDataSourceFactory(), downloadManager); + new DownloadTracker(context, buildDataSourceFactory(context), downloadManager); } } - private void upgradeActionFile( - String fileName, DefaultDownloadIndex downloadIndex, boolean addNewDownloadsAsCompleted) { + private static synchronized void upgradeActionFile( + Context context, + String fileName, + DefaultDownloadIndex downloadIndex, + boolean addNewDownloadsAsCompleted) { try { ActionFileUpgradeUtil.upgradeAndDelete( - new File(getDownloadDirectory(), fileName), + new File(getDownloadDirectory(context), fileName), /* downloadIdProvider= */ null, downloadIndex, /* deleteOnFailure= */ true, @@ -156,18 +172,18 @@ public class DemoApplication extends MultiDexApplication { } } - private DatabaseProvider getDatabaseProvider() { + private static synchronized DatabaseProvider getDatabaseProvider(Context context) { if (databaseProvider == null) { - databaseProvider = new ExoDatabaseProvider(this); + databaseProvider = new ExoDatabaseProvider(context); } return databaseProvider; } - private File getDownloadDirectory() { + private static synchronized File getDownloadDirectory(Context context) { if (downloadDirectory == null) { - downloadDirectory = getExternalFilesDir(null); + downloadDirectory = context.getExternalFilesDir(/* type= */ null); if (downloadDirectory == null) { - downloadDirectory = getFilesDir(); + downloadDirectory = context.getFilesDir(); } } return downloadDirectory; @@ -181,4 +197,6 @@ public class DemoApplication extends MultiDexApplication { .setCacheWriteDataSinkFactory(null) .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR); } + + private DemoUtil() {} } diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 3f80390618..8ddecdad59 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -315,7 +315,7 @@ public class PlayerActivity extends AppCompatActivity boolean preferExtensionDecoders = intent.getBooleanExtra(IntentUtil.PREFER_EXTENSION_DECODERS_EXTRA, false); RenderersFactory renderersFactory = - ((DemoApplication) getApplication()).buildRenderersFactory(preferExtensionDecoders); + DemoUtil.buildRenderersFactory(/* context= */ this, preferExtensionDecoders); trackSelector = new DefaultTrackSelector(/* context= */ this, trackSelectionFactory); trackSelector.setParameters(trackSelectorParameters); @@ -357,7 +357,7 @@ public class PlayerActivity extends AppCompatActivity List mediaItems = IntentUtil.createMediaItemsFromIntent( - intent, ((DemoApplication) getApplication()).getDownloadTracker()); + intent, DemoUtil.getDownloadTracker(/* context= */ this)); boolean hasAds = false; for (int i = 0; i < mediaItems.size(); i++) { MediaItem mediaItem = mediaItems.get(i); @@ -439,7 +439,7 @@ public class PlayerActivity extends AppCompatActivity /** Returns a new DataSource factory. */ protected DataSource.Factory buildDataSourceFactory() { - return ((DemoApplication) getApplication()).buildDataSourceFactory(); + return DemoUtil.buildDataSourceFactory(/* context= */ this); } // User controls diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java index 036ea47f7e..ae79fb84f5 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java @@ -114,9 +114,8 @@ public class SampleChooserActivity extends AppCompatActivity Arrays.sort(uris); } - DemoApplication application = (DemoApplication) getApplication(); - useExtensionRenderers = application.useExtensionRenderers(); - downloadTracker = application.getDownloadTracker(); + useExtensionRenderers = DemoUtil.useExtensionRenderers(); + downloadTracker = DemoUtil.getDownloadTracker(/* context= */ this); loadSample(); // Start the download service if it should be running but it's not currently. @@ -247,8 +246,8 @@ public class SampleChooserActivity extends AppCompatActivity .show(); } else { RenderersFactory renderersFactory = - ((DemoApplication) getApplication()) - .buildRenderersFactory(isNonNullAndChecked(preferExtensionDecodersMenuItem)); + DemoUtil.buildRenderersFactory( + /* context= */ this, isNonNullAndChecked(preferExtensionDecodersMenuItem)); downloadTracker.toggleDownload( getSupportFragmentManager(), playlistHolder.mediaItems.get(0), renderersFactory); } 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 178cd44dd3..83da4d54a8 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 @@ -31,7 +31,6 @@ public final class DownloadNotificationHelper { private static final @StringRes int NULL_STRING_ID = 0; - private final Context context; private final NotificationCompat.Builder notificationBuilder; /** @@ -39,14 +38,14 @@ public final class DownloadNotificationHelper { * @param channelId The id of the notification channel to use. */ public DownloadNotificationHelper(Context context, String channelId) { - context = context.getApplicationContext(); - this.context = context; - this.notificationBuilder = new NotificationCompat.Builder(context, channelId); + this.notificationBuilder = + new NotificationCompat.Builder(context.getApplicationContext(), channelId); } /** * Returns a progress notification for the given downloads. * + * @param context A context. * @param smallIcon A small icon for the notification. * @param contentIntent An optional content intent to send when the notification is clicked. * @param message An optional message to display on the notification. @@ -54,6 +53,7 @@ public final class DownloadNotificationHelper { * @return The notification. */ public Notification buildProgressNotification( + Context context, @DrawableRes int smallIcon, @Nullable PendingIntent contentIntent, @Nullable String message, @@ -95,6 +95,7 @@ public final class DownloadNotificationHelper { indeterminate = allDownloadPercentagesUnknown && haveDownloadedBytes; } return buildNotification( + context, smallIcon, contentIntent, message, @@ -109,37 +110,47 @@ public final class DownloadNotificationHelper { /** * Returns a notification for a completed download. * + * @param context A context. * @param smallIcon A small icon for the notifications. * @param contentIntent An optional content intent to send when the notification is clicked. * @param message An optional message to display on the notification. * @return The notification. */ public Notification buildDownloadCompletedNotification( - @DrawableRes int smallIcon, @Nullable PendingIntent contentIntent, @Nullable String message) { + Context context, + @DrawableRes int smallIcon, + @Nullable PendingIntent contentIntent, + @Nullable String message) { int titleStringId = R.string.exo_download_completed; - return buildEndStateNotification(smallIcon, contentIntent, message, titleStringId); + return buildEndStateNotification(context, smallIcon, contentIntent, message, titleStringId); } /** * Returns a notification for a failed download. * + * @param context A context. * @param smallIcon A small icon for the notifications. * @param contentIntent An optional content intent to send when the notification is clicked. * @param message An optional message to display on the notification. * @return The notification. */ public Notification buildDownloadFailedNotification( - @DrawableRes int smallIcon, @Nullable PendingIntent contentIntent, @Nullable String message) { + Context context, + @DrawableRes int smallIcon, + @Nullable PendingIntent contentIntent, + @Nullable String message) { @StringRes int titleStringId = R.string.exo_download_failed; - return buildEndStateNotification(smallIcon, contentIntent, message, titleStringId); + return buildEndStateNotification(context, smallIcon, contentIntent, message, titleStringId); } private Notification buildEndStateNotification( + Context context, @DrawableRes int smallIcon, @Nullable PendingIntent contentIntent, @Nullable String message, @StringRes int titleStringId) { return buildNotification( + context, smallIcon, contentIntent, message, @@ -152,6 +163,7 @@ public final class DownloadNotificationHelper { } private Notification buildNotification( + Context context, @DrawableRes int smallIcon, @Nullable PendingIntent contentIntent, @Nullable String message, diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java index 223a97f69c..8c03dbea42 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java @@ -52,7 +52,7 @@ public final class DownloadNotificationUtil { @Nullable String message, List downloads) { return new DownloadNotificationHelper(context, channelId) - .buildProgressNotification(smallIcon, contentIntent, message, downloads); + .buildProgressNotification(context, smallIcon, contentIntent, message, downloads); } /** @@ -72,7 +72,7 @@ public final class DownloadNotificationUtil { @Nullable PendingIntent contentIntent, @Nullable String message) { return new DownloadNotificationHelper(context, channelId) - .buildDownloadCompletedNotification(smallIcon, contentIntent, message); + .buildDownloadCompletedNotification(context, smallIcon, contentIntent, message); } /** @@ -92,6 +92,6 @@ public final class DownloadNotificationUtil { @Nullable PendingIntent contentIntent, @Nullable String message) { return new DownloadNotificationHelper(context, channelId) - .buildDownloadFailedNotification(smallIcon, contentIntent, message); + .buildDownloadFailedNotification(context, smallIcon, contentIntent, message); } }