Use DataSourceBitmapLoader by default

This replaces the SimpleBitmapLoader that can now be deprecated
as it's fully unused and doesn't provide any additional functionality.

#minor-release

PiperOrigin-RevId: 574454636
(cherry picked from commit db86932781b4a5f377d1f4c1414c3d6a74ede174)
This commit is contained in:
tonihei 2023-10-18 06:15:30 -07:00 committed by Rohit Singh
parent 5a4adc9b25
commit fe60d0d7b4
13 changed files with 122 additions and 107 deletions

View File

@ -32,6 +32,8 @@
framework session, this has the effect that the `queue` button in the UI framework session, this has the effect that the `queue` button in the UI
of Android Auto is not displayed of Android Auto is not displayed
(([#339](https://github.com/androidx/media/issues/339)). (([#339](https://github.com/androidx/media/issues/339)).
* Use `DataSourceBitmapLoader` by default instead of `SimpleBitmapLoader`
([#271](https://github.com/androidx/media/issues/271),[#327](https://github.com/androidx/media/issues/327)).
* UI: * UI:
* Downloads: * Downloads:
* OkHttp Extension: * OkHttp Extension:

View File

@ -27,11 +27,9 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.media3.common.AudioAttributes import androidx.media3.common.AudioAttributes
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.datasource.DataSourceBitmapLoader
import androidx.media3.demo.session.service.R import androidx.media3.demo.session.service.R
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.util.EventLogger import androidx.media3.exoplayer.util.EventLogger
import androidx.media3.session.CacheBitmapLoader
import androidx.media3.session.MediaLibraryService import androidx.media3.session.MediaLibraryService
import androidx.media3.session.MediaSession import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSession.ControllerInfo import androidx.media3.session.MediaSession.ControllerInfo
@ -117,14 +115,8 @@ open class DemoPlaybackService : MediaLibraryService() {
.build() .build()
player.addAnalyticsListener(EventLogger()) player.addAnalyticsListener(EventLogger())
// MediaLibrarySession.Builder.setCustomLayout
// MediaLibrarySession.Builder.setBitmapLoader
// CacheBitmapLoader
// DataSourceBitmapLoader
@OptIn(UnstableApi::class)
mediaLibrarySession = mediaLibrarySession =
MediaLibrarySession.Builder(this, player, createLibrarySessionCallback()) MediaLibrarySession.Builder(this, player, createLibrarySessionCallback())
.setBitmapLoader(CacheBitmapLoader(DataSourceBitmapLoader(/* context= */ this)))
.also { builder -> getSingleTopActivity()?.let { builder.setSessionActivity(it) } } .also { builder -> getSingleTopActivity()?.let { builder.setSessionActivity(it) } }
.build() .build()
} }

View File

@ -41,6 +41,7 @@ dependencies {
api project(modulePrefix + 'lib-common') api project(modulePrefix + 'lib-common')
compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion compileOnly 'com.google.errorprone:error_prone_annotations:' + errorProneVersion
compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion
implementation project(modulePrefix + 'lib-datasource')
implementation 'androidx.collection:collection:' + androidxCollectionVersion implementation 'androidx.collection:collection:' + androidxCollectionVersion
implementation 'androidx.media:media:' + androidxMediaVersion implementation 'androidx.media:media:' + androidxMediaVersion
implementation 'androidx.core:core:' + androidxCoreVersion implementation 'androidx.core:core:' + androidxCoreVersion

View File

@ -35,6 +35,7 @@ import androidx.media3.common.util.BitmapLoader;
import androidx.media3.common.util.Consumer; import androidx.media3.common.util.Consumer;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.session.MediaLibraryService.LibraryParams; import androidx.media3.session.MediaLibraryService.LibraryParams;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -126,8 +127,8 @@ public final class MediaBrowser extends MediaController {
/** /**
* Sets a {@link BitmapLoader} for the {@link MediaBrowser} to decode bitmaps from compressed * Sets a {@link BitmapLoader} for the {@link MediaBrowser} to decode bitmaps from compressed
* binary data. If not set, a {@link CacheBitmapLoader} that wraps a {@link SimpleBitmapLoader} * binary data. If not set, a {@link CacheBitmapLoader} that wraps a {@link
* will be used. * DataSourceBitmapLoader} will be used.
* *
* @param bitmapLoader The bitmap loader. * @param bitmapLoader The bitmap loader.
* @return The builder to allow chaining. * @return The builder to allow chaining.
@ -168,7 +169,7 @@ public final class MediaBrowser extends MediaController {
public ListenableFuture<MediaBrowser> buildAsync() { public ListenableFuture<MediaBrowser> buildAsync() {
MediaControllerHolder<MediaBrowser> holder = new MediaControllerHolder<>(applicationLooper); MediaControllerHolder<MediaBrowser> holder = new MediaControllerHolder<>(applicationLooper);
if (token.isLegacySession() && bitmapLoader == null) { if (token.isLegacySession() && bitmapLoader == null) {
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
MediaBrowser browser = MediaBrowser browser =
new MediaBrowser( new MediaBrowser(

View File

@ -58,6 +58,7 @@ import androidx.media3.common.util.Log;
import androidx.media3.common.util.Size; import androidx.media3.common.util.Size;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
@ -269,8 +270,8 @@ public class MediaController implements Player {
/** /**
* Sets a {@link BitmapLoader} for the {@link MediaController} to decode bitmaps from compressed * Sets a {@link BitmapLoader} for the {@link MediaController} to decode bitmaps from compressed
* binary data. If not set, a {@link CacheBitmapLoader} that wraps a {@link SimpleBitmapLoader} * binary data. If not set, a {@link CacheBitmapLoader} that wraps a {@link
* will be used. * DataSourceBitmapLoader} will be used.
* *
* @param bitmapLoader The bitmap loader. * @param bitmapLoader The bitmap loader.
* @return The builder to allow chaining. * @return The builder to allow chaining.
@ -312,7 +313,7 @@ public class MediaController implements Player {
MediaControllerHolder<MediaController> holder = MediaControllerHolder<MediaController> holder =
new MediaControllerHolder<>(applicationLooper); new MediaControllerHolder<>(applicationLooper);
if (token.isLegacySession() && bitmapLoader == null) { if (token.isLegacySession() && bitmapLoader == null) {
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
MediaController controller = MediaController controller =
new MediaController( new MediaController(

View File

@ -38,6 +38,7 @@ import androidx.media3.common.Player;
import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.BitmapLoader;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.session.MediaSession.ControllerInfo; import androidx.media3.session.MediaSession.ControllerInfo;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -453,15 +454,14 @@ public abstract class MediaLibraryService extends MediaSessionService {
/** /**
* Sets a {@link BitmapLoader} for the {@link MediaLibrarySession} to decode bitmaps from * Sets a {@link BitmapLoader} for the {@link MediaLibrarySession} to decode bitmaps from
* compressed binary data or load bitmaps from {@link Uri}. If not set, a {@link * compressed binary data or load bitmaps from {@link Uri}.
* CacheBitmapLoader} with a {@link SimpleBitmapLoader} inside will be used.
* *
* <p>The provided instance will likely be called repeatedly with the same request, so it * <p>The provided instance will likely be called repeatedly with the same request, so it
* would be best if any provided instance does some caching. Simple caching can be added to * would be best if any provided instance does some caching. Simple caching can be added to
* any {@link BitmapLoader} implementation by wrapping it in {@link CacheBitmapLoader} before * any {@link BitmapLoader} implementation by wrapping it in {@link CacheBitmapLoader} before
* passing it to this method. * passing it to this method.
* *
* <p>If no instance is set, a {@link CacheBitmapLoader} with a {@link SimpleBitmapLoader} * <p>If no instance is set, a {@link CacheBitmapLoader} with a {@link DataSourceBitmapLoader}
* inside will be used. * inside will be used.
* *
* @param bitmapLoader The bitmap loader {@link BitmapLoader}. * @param bitmapLoader The bitmap loader {@link BitmapLoader}.
@ -542,7 +542,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
@Override @Override
public MediaLibrarySession build() { public MediaLibrarySession build() {
if (bitmapLoader == null) { if (bitmapLoader == null) {
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
return new MediaLibrarySession( return new MediaLibrarySession(
context, context,

View File

@ -58,6 +58,7 @@ import androidx.media3.common.VideoSize;
import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.BitmapLoader;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.session.MediaLibraryService.LibraryParams; import androidx.media3.session.MediaLibraryService.LibraryParams;
import androidx.media3.session.MediaLibraryService.MediaLibrarySession; import androidx.media3.session.MediaLibraryService.MediaLibrarySession;
import com.google.common.base.Objects; import com.google.common.base.Objects;
@ -330,15 +331,14 @@ public class MediaSession {
/** /**
* Sets a {@link BitmapLoader} for the {@link MediaSession} to decode bitmaps from compressed * Sets a {@link BitmapLoader} for the {@link MediaSession} to decode bitmaps from compressed
* binary data or load bitmaps from {@link Uri}. If not set, a {@link CacheBitmapLoader} with a * binary data or load bitmaps from {@link Uri}.
* {@link SimpleBitmapLoader} inside will be used.
* *
* <p>The provided instance will likely be called repeatedly with the same request, so it would * <p>The provided instance will likely be called repeatedly with the same request, so it would
* be best if any provided instance does some caching. Simple caching can be added to any {@link * be best if any provided instance does some caching. Simple caching can be added to any {@link
* BitmapLoader} implementation by wrapping it in {@link CacheBitmapLoader} before passing it to * BitmapLoader} implementation by wrapping it in {@link CacheBitmapLoader} before passing it to
* this method. * this method.
* *
* <p>If no instance is set, a {@link CacheBitmapLoader} with a {@link SimpleBitmapLoader} * <p>If no instance is set, a {@link CacheBitmapLoader} with a {@link DataSourceBitmapLoader}
* inside will be used. * inside will be used.
* *
* @param bitmapLoader The bitmap loader {@link BitmapLoader}. * @param bitmapLoader The bitmap loader {@link BitmapLoader}.
@ -416,7 +416,7 @@ public class MediaSession {
@Override @Override
public MediaSession build() { public MediaSession build() {
if (bitmapLoader == null) { if (bitmapLoader == null) {
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
return new MediaSession( return new MediaSession(
context, context,

View File

@ -39,17 +39,9 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
/** /**
* A simple bitmap loader that delegates all tasks to an executor and supports fetching images from * @deprecated Use {@link androidx.media3.datasource.DataSourceBitmapLoader} instead.
* URIs with {@code file}, {@code http} and {@code https} schemes.
*
* <p>Loading tasks are delegated to an {@link ExecutorService} (or {@link
* ListeningExecutorService}) defined during construction. If no executor service is defined, all
* tasks are delegated to a single-thread executor service that is shared between instances of this
* class.
*
* <p>For HTTP(S) transfers, this class reads a resource only when the endpoint responds with an
* {@code HTTP 200} after sending the HTTP request.
*/ */
@Deprecated
@UnstableApi @UnstableApi
public final class SimpleBitmapLoader implements BitmapLoader { public final class SimpleBitmapLoader implements BitmapLoader {

View File

@ -20,18 +20,22 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.robolectric.annotation.GraphicsMode.Mode.NATIVE; import static org.robolectric.annotation.GraphicsMode.Mode.NATIVE;
import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri; import android.net.Uri;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.test.utils.TestUtil; import androidx.media3.test.utils.TestUtil;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.IOException;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.MockWebServer;
import okio.Buffer; import okio.Buffer;
import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@ -54,21 +58,26 @@ public class CacheBitmapLoaderTest {
@Rule public final TemporaryFolder tempFolder = new TemporaryFolder(); @Rule public final TemporaryFolder tempFolder = new TemporaryFolder();
private Context context;
@Before
public void setUp() {
context = ApplicationProvider.getApplicationContext();
}
@Test @Test
public void decodeBitmap_requestWithSameDataTwice_success() throws Exception { public void decodeBitmap_requestWithSameDataTwice_success() throws Exception {
CacheBitmapLoader cacheBitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); CacheBitmapLoader cacheBitmapLoader =
byte[] imageData = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); byte[] imageData = TestUtil.getByteArray(context, TEST_IMAGE_PATH);
Bitmap expectedBitmap =
apply90DegreeExifRotation(
BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length));
// First request, no cached bitmap load request. // First request, no cached bitmap load request.
ListenableFuture<Bitmap> future1 = cacheBitmapLoader.decodeBitmap(imageData); ListenableFuture<Bitmap> future1 = cacheBitmapLoader.decodeBitmap(imageData);
assertThat( assertThat(future1.get(10, SECONDS).sameAs(expectedBitmap)).isTrue();
future1
.get(10, SECONDS)
.sameAs(
BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length)))
.isTrue();
// Second request, has cached bitmap load request. // Second request, has cached bitmap load request.
ListenableFuture<Bitmap> future2 = cacheBitmapLoader.decodeBitmap(imageData); ListenableFuture<Bitmap> future2 = cacheBitmapLoader.decodeBitmap(imageData);
@ -78,37 +87,33 @@ public class CacheBitmapLoaderTest {
@Test @Test
public void decodeBitmap_requestWithDifferentData_success() throws Exception { public void decodeBitmap_requestWithDifferentData_success() throws Exception {
CacheBitmapLoader cacheBitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); CacheBitmapLoader cacheBitmapLoader =
byte[] imageData1 = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); byte[] imageData1 = TestUtil.getByteArray(context, TEST_IMAGE_PATH);
byte[] imageData2 = byte[] imageData2 = TestUtil.getByteArray(context, SECOND_TEST_IMAGE_PATH);
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SECOND_TEST_IMAGE_PATH); Bitmap expectedBitmap1 =
apply90DegreeExifRotation(
BitmapFactory.decodeByteArray(imageData1, /* offset= */ 0, imageData1.length));
Bitmap expectedBitmap2 =
apply90DegreeExifRotation(
BitmapFactory.decodeByteArray(imageData2, /* offset= */ 0, imageData2.length));
// First request. // First request.
ListenableFuture<Bitmap> future1 = cacheBitmapLoader.decodeBitmap(imageData1); ListenableFuture<Bitmap> future1 = cacheBitmapLoader.decodeBitmap(imageData1);
assertThat( assertThat(future1.get(10, SECONDS).sameAs(expectedBitmap1)).isTrue();
future1
.get(10, SECONDS)
.sameAs(
BitmapFactory.decodeByteArray(imageData1, /* offset= */ 0, imageData1.length)))
.isTrue();
// Second request. // Second request.
ListenableFuture<Bitmap> future2 = cacheBitmapLoader.decodeBitmap(imageData2); ListenableFuture<Bitmap> future2 = cacheBitmapLoader.decodeBitmap(imageData2);
assertThat( assertThat(future2.get(10, SECONDS).sameAs(expectedBitmap2)).isTrue();
future2
.get(10, SECONDS)
.sameAs(
BitmapFactory.decodeByteArray(imageData2, /* offset= */ 0, imageData2.length)))
.isTrue();
assertThat(future1).isNotSameInstanceAs(future2); assertThat(future1).isNotSameInstanceAs(future2);
} }
@Test @Test
public void decodeBitmap_requestWithSameDataTwice_throwsException() { public void decodeBitmap_requestWithSameDataTwice_throwsException() {
CacheBitmapLoader cacheBitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); CacheBitmapLoader cacheBitmapLoader =
new CacheBitmapLoader(new DataSourceBitmapLoader(context));
// First request, no cached bitmap load request. // First request, no cached bitmap load request.
ListenableFuture<Bitmap> future1 = cacheBitmapLoader.decodeBitmap(new byte[0]); ListenableFuture<Bitmap> future1 = cacheBitmapLoader.decodeBitmap(new byte[0]);
@ -125,39 +130,35 @@ public class CacheBitmapLoaderTest {
@Test @Test
public void loadBitmap_httpUri_requestWithSameUriTwice_success() throws Exception { public void loadBitmap_httpUri_requestWithSameUriTwice_success() throws Exception {
CacheBitmapLoader cacheBitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); CacheBitmapLoader cacheBitmapLoader =
byte[] imageData = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); byte[] imageData = TestUtil.getByteArray(context, TEST_IMAGE_PATH);
Buffer responseBody = new Buffer().write(imageData); Buffer responseBody = new Buffer().write(imageData);
MockWebServer mockWebServer = new MockWebServer(); MockWebServer mockWebServer = new MockWebServer();
mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody)); mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody));
Uri uri = Uri.parse(mockWebServer.url("test_path").toString()); Uri uri = Uri.parse(mockWebServer.url("test_path").toString());
Bitmap expectedBitmap =
apply90DegreeExifRotation(
BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length));
// First request, no cached bitmap load request. // First request, no cached bitmap load request.
Bitmap bitmap = cacheBitmapLoader.loadBitmap(uri).get(10, SECONDS); Bitmap bitmap = cacheBitmapLoader.loadBitmap(uri).get(10, SECONDS);
assertThat( assertThat(bitmap.sameAs(expectedBitmap)).isTrue();
bitmap.sameAs(
BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length)))
.isTrue();
// Second request, has cached bitmap load request. // Second request, has cached bitmap load request.
bitmap = cacheBitmapLoader.loadBitmap(uri).get(10, SECONDS); bitmap = cacheBitmapLoader.loadBitmap(uri).get(10, SECONDS);
assertThat( assertThat(bitmap.sameAs(expectedBitmap)).isTrue();
bitmap.sameAs(
BitmapFactory.decodeByteArray(imageData, /* offset= */ 0, imageData.length)))
.isTrue();
assertThat(mockWebServer.getRequestCount()).isEqualTo(1); assertThat(mockWebServer.getRequestCount()).isEqualTo(1);
} }
@Test @Test
public void loadBitmap_httpUri_requestWithDifferentUri_success() throws Exception { public void loadBitmap_httpUri_requestWithDifferentUri_success() throws Exception {
CacheBitmapLoader cacheBitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); CacheBitmapLoader cacheBitmapLoader =
byte[] imageData1 = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TEST_IMAGE_PATH); byte[] imageData1 = TestUtil.getByteArray(context, TEST_IMAGE_PATH);
byte[] imageData2 = byte[] imageData2 = TestUtil.getByteArray(context, SECOND_TEST_IMAGE_PATH);
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SECOND_TEST_IMAGE_PATH);
Buffer responseBody1 = new Buffer().write(imageData1); Buffer responseBody1 = new Buffer().write(imageData1);
Buffer responseBody2 = new Buffer().write(imageData2); Buffer responseBody2 = new Buffer().write(imageData2);
MockWebServer mockWebServer = new MockWebServer(); MockWebServer mockWebServer = new MockWebServer();
@ -165,28 +166,29 @@ public class CacheBitmapLoaderTest {
mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody2)); mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(responseBody2));
Uri uri1 = Uri.parse(mockWebServer.url("test_path_1").toString()); Uri uri1 = Uri.parse(mockWebServer.url("test_path_1").toString());
Uri uri2 = Uri.parse(mockWebServer.url("test_path_2").toString()); Uri uri2 = Uri.parse(mockWebServer.url("test_path_2").toString());
Bitmap expectedBitmap1 =
apply90DegreeExifRotation(
BitmapFactory.decodeByteArray(imageData1, /* offset= */ 0, imageData1.length));
Bitmap expectedBitmap2 =
apply90DegreeExifRotation(
BitmapFactory.decodeByteArray(imageData2, /* offset= */ 0, imageData2.length));
// First request. // First request.
Bitmap bitmap = cacheBitmapLoader.loadBitmap(uri1).get(10, SECONDS); Bitmap bitmap = cacheBitmapLoader.loadBitmap(uri1).get(10, SECONDS);
assertThat( assertThat(bitmap.sameAs(expectedBitmap1)).isTrue();
bitmap.sameAs(
BitmapFactory.decodeByteArray(imageData1, /* offset= */ 0, imageData1.length)))
.isTrue();
// Second request. // Second request.
bitmap = cacheBitmapLoader.loadBitmap(uri2).get(10, SECONDS); bitmap = cacheBitmapLoader.loadBitmap(uri2).get(10, SECONDS);
assertThat( assertThat(bitmap.sameAs(expectedBitmap2)).isTrue();
bitmap.sameAs(
BitmapFactory.decodeByteArray(imageData2, /* offset= */ 0, imageData2.length)))
.isTrue();
assertThat(mockWebServer.getRequestCount()).isEqualTo(2); assertThat(mockWebServer.getRequestCount()).isEqualTo(2);
} }
@Test @Test
public void loadBitmap_httpUri_requestWithSameUriTwice_throwsException() throws Exception { public void loadBitmap_httpUri_requestWithSameUriTwice_throwsException() {
CacheBitmapLoader cacheBitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); CacheBitmapLoader cacheBitmapLoader =
new CacheBitmapLoader(new DataSourceBitmapLoader(context));
MockWebServer mockWebServer = new MockWebServer(); MockWebServer mockWebServer = new MockWebServer();
mockWebServer.enqueue(new MockResponse().setResponseCode(404)); mockWebServer.enqueue(new MockResponse().setResponseCode(404));
Uri uri = Uri.parse(mockWebServer.url("test_path").toString()); Uri uri = Uri.parse(mockWebServer.url("test_path").toString());
@ -201,10 +203,25 @@ public class CacheBitmapLoaderTest {
assertThrows(ExecutionException.class, () -> future1.get(10, SECONDS)); assertThrows(ExecutionException.class, () -> future1.get(10, SECONDS));
ExecutionException executionException2 = ExecutionException executionException2 =
assertThrows(ExecutionException.class, () -> future2.get(10, SECONDS)); assertThrows(ExecutionException.class, () -> future2.get(10, SECONDS));
assertThat(executionException1).hasCauseThat().isInstanceOf(IOException.class); assertThat(executionException1)
assertThat(executionException2).hasCauseThat().isInstanceOf(IOException.class); .hasCauseThat()
assertThat(executionException1).hasMessageThat().contains("Invalid response status"); .isInstanceOf(HttpDataSource.InvalidResponseCodeException.class);
assertThat(executionException2).hasMessageThat().contains("Invalid response status"); assertThat(executionException2)
.hasCauseThat()
.isInstanceOf(HttpDataSource.InvalidResponseCodeException.class);
assertThat(mockWebServer.getRequestCount()).isEqualTo(1); assertThat(mockWebServer.getRequestCount()).isEqualTo(1);
} }
private static Bitmap apply90DegreeExifRotation(Bitmap bitmap) {
Matrix rotationMatrix = new Matrix();
rotationMatrix.postRotate(/* degrees= */ 90);
return Bitmap.createBitmap(
bitmap,
/* x= */ 0,
/* y= */ 0,
bitmap.getWidth(),
bitmap.getHeight(),
rotationMatrix,
/* filter= */ false);
}
} }

View File

@ -45,6 +45,7 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.GraphicsMode; import org.robolectric.annotation.GraphicsMode;
/** Tests for {@link SimpleBitmapLoader}. */ /** Tests for {@link SimpleBitmapLoader}. */
@SuppressWarnings("deprecation") // Testing deprecated class
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@GraphicsMode(value = NATIVE) @GraphicsMode(value = NATIVE)
public class SimpleBitmapLoaderTest { public class SimpleBitmapLoaderTest {

View File

@ -47,6 +47,7 @@ import androidx.media3.common.Timeline;
import androidx.media3.common.Timeline.Window; import androidx.media3.common.Timeline.Window;
import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.BitmapLoader;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -84,7 +85,7 @@ public class MediaControllerMediaSessionCompatCallbackAggregationTest {
public void setUp() throws Exception { public void setUp() throws Exception {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
session = new RemoteMediaSessionCompat(DEFAULT_TEST_NAME, context); session = new RemoteMediaSessionCompat(DEFAULT_TEST_NAME, context);
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
@After @After

View File

@ -71,6 +71,7 @@ import androidx.media3.common.Player.State;
import androidx.media3.common.Timeline; import androidx.media3.common.Timeline;
import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.BitmapLoader;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.test.session.common.HandlerThreadTestRule; import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.media3.test.session.common.MainLooperTestRule; import androidx.media3.test.session.common.MainLooperTestRule;
import androidx.media3.test.session.common.MockActivity; import androidx.media3.test.session.common.MockActivity;
@ -126,7 +127,7 @@ public class MediaControllerWithMediaSessionCompatTest {
public void setUp() throws Exception { public void setUp() throws Exception {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
session = new RemoteMediaSessionCompat(DEFAULT_TEST_NAME, context); session = new RemoteMediaSessionCompat(DEFAULT_TEST_NAME, context);
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
@After @After
@ -986,8 +987,10 @@ public class MediaControllerWithMediaSessionCompatTest {
MediaMetadata mediaMetadata = MediaMetadata mediaMetadata =
threadTestRule.getHandler().postAndSync(controller::getMediaMetadata); threadTestRule.getHandler().postAndSync(controller::getMediaMetadata);
assertThat(mediaMetadata.title).isEqualTo(testMediaDescriptionCompat.getTitle()); assertThat(mediaMetadata.title.toString())
assertThat(mediaMetadata.description).isEqualTo(testMediaDescriptionCompat.getDescription()); .isEqualTo(testMediaDescriptionCompat.getTitle().toString());
assertThat(mediaMetadata.description.toString())
.isEqualTo(testMediaDescriptionCompat.getDescription().toString());
} }
@Test @Test
@ -1653,7 +1656,7 @@ public class MediaControllerWithMediaSessionCompatTest {
VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE,
/* maxVolume= */ 100, /* maxVolume= */ 100,
/* currentVolume= */ 45, /* currentVolume= */ 45,
/* routingSessionId= */ "route"); /* routingControllerId= */ "route");
int testLocalStreamType = AudioManager.STREAM_ALARM; int testLocalStreamType = AudioManager.STREAM_ALARM;
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

View File

@ -60,6 +60,7 @@ import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackGroup;
import androidx.media3.common.Tracks; import androidx.media3.common.Tracks;
import androidx.media3.common.util.BitmapLoader; import androidx.media3.common.util.BitmapLoader;
import androidx.media3.datasource.DataSourceBitmapLoader;
import androidx.media3.session.PlayerInfo.BundlingExclusions; import androidx.media3.session.PlayerInfo.BundlingExclusions;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -85,7 +86,7 @@ public final class MediaUtilsTest {
@Before @Before
public void setUp() { public void setUp() {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
bitmapLoader = new CacheBitmapLoader(new SimpleBitmapLoader()); bitmapLoader = new CacheBitmapLoader(new DataSourceBitmapLoader(context));
} }
@Test @Test
@ -99,7 +100,7 @@ public final class MediaUtilsTest {
MediaItem mediaItem = MediaUtils.convertToMediaItem(browserItem); MediaItem mediaItem = MediaUtils.convertToMediaItem(browserItem);
assertThat(mediaItem.mediaId).isEqualTo(mediaId); assertThat(mediaItem.mediaId).isEqualTo(mediaId);
assertThat(mediaItem.mediaMetadata.title).isEqualTo(title); assertThat(mediaItem.mediaMetadata.title.toString()).isEqualTo(title);
} }
@Test @Test
@ -161,8 +162,8 @@ public final class MediaUtilsTest {
MediaUtils.convertToMediaDescriptionCompat(mediaItem, /* artworkBitmap= */ null); MediaUtils.convertToMediaDescriptionCompat(mediaItem, /* artworkBitmap= */ null);
assertThat(descriptionCompat.getMediaId()).isEqualTo(mediaId); assertThat(descriptionCompat.getMediaId()).isEqualTo(mediaId);
assertThat(descriptionCompat.getTitle()).isEqualTo(title); assertThat(descriptionCompat.getTitle().toString()).isEqualTo(title);
assertThat(descriptionCompat.getDescription()).isEqualTo(description); assertThat(descriptionCompat.getDescription().toString()).isEqualTo(description);
assertThat( assertThat(
descriptionCompat descriptionCompat
.getExtras() .getExtras()
@ -203,8 +204,8 @@ public final class MediaUtilsTest {
@Test @Test
public void convertToMediaMetadata_withTitle() { public void convertToMediaMetadata_withTitle() {
CharSequence title = "title"; String title = "title";
assertThat(MediaUtils.convertToMediaMetadata(title).title).isEqualTo(title); assertThat(MediaUtils.convertToMediaMetadata(title).title.toString()).isEqualTo(title);
} }
@Test @Test
@ -268,7 +269,7 @@ public final class MediaUtilsTest {
mediaItem.mediaMetadata, mediaItem.mediaMetadata,
"mediaId", "mediaId",
Uri.parse("http://www.example.com"), Uri.parse("http://www.example.com"),
/* durotionsMs= */ C.TIME_UNSET, /* durationMs= */ C.TIME_UNSET,
/* artworkBitmap= */ null); /* artworkBitmap= */ null);
assertThat( assertThat(
@ -476,7 +477,7 @@ public final class MediaUtilsTest {
@Test @Test
public void convertToSessionCommands_whenSessionIsNotReadyOnSdk21_disallowsRating() { public void convertToSessionCommands_whenSessionIsNotReadyOnSdk21_disallowsRating() {
SessionCommands sessionCommands = SessionCommands sessionCommands =
MediaUtils.convertToSessionCommands(/* playbackState= */ null, /* isSessionReady= */ false); MediaUtils.convertToSessionCommands(/* state= */ null, /* isSessionReady= */ false);
assertThat(sessionCommands.contains(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)).isFalse(); assertThat(sessionCommands.contains(SessionCommand.COMMAND_CODE_SESSION_SET_RATING)).isFalse();
} }
@ -946,7 +947,7 @@ public final class MediaUtilsTest {
long currentPositionMs = long currentPositionMs =
MediaUtils.convertToCurrentPositionMs( MediaUtils.convertToCurrentPositionMs(
/* playbackStateCompat= */ null, /* playbackStateCompat= */ null,
/* currentMediaMetadata= */ null, /* metadataCompat= */ null,
/* timeDiffMs= */ C.TIME_UNSET); /* timeDiffMs= */ C.TIME_UNSET);
assertThat(currentPositionMs).isEqualTo(0); assertThat(currentPositionMs).isEqualTo(0);
} }
@ -1113,7 +1114,8 @@ public final class MediaUtilsTest {
oldPlayerInfo, oldPlayerInfo,
BundlingExclusions.NONE, BundlingExclusions.NONE,
newPlayerInfo, newPlayerInfo,
new BundlingExclusions(/* isTimelineExcluded= */ true, /* areTracksExcluded= */ true), new BundlingExclusions(
/* isTimelineExcluded= */ true, /* areCurrentTracksExcluded= */ true),
availableCommands); availableCommands);
assertThat(mergeResult.first.timeline).isSameInstanceAs(oldPlayerInfo.timeline); assertThat(mergeResult.first.timeline).isSameInstanceAs(oldPlayerInfo.timeline);
@ -1155,7 +1157,8 @@ public final class MediaUtilsTest {
oldPlayerInfo, oldPlayerInfo,
BundlingExclusions.NONE, BundlingExclusions.NONE,
newPlayerInfo, newPlayerInfo,
new BundlingExclusions(/* isTimelineExcluded= */ true, /* areTracksExcluded= */ true), new BundlingExclusions(
/* isTimelineExcluded= */ true, /* areCurrentTracksExcluded= */ true),
availableCommands); availableCommands);
assertThat(mergeResult.first.timeline).isSameInstanceAs(Timeline.EMPTY); assertThat(mergeResult.first.timeline).isSameInstanceAs(Timeline.EMPTY);
@ -1197,7 +1200,8 @@ public final class MediaUtilsTest {
oldPlayerInfo, oldPlayerInfo,
BundlingExclusions.NONE, BundlingExclusions.NONE,
newPlayerInfo, newPlayerInfo,
new BundlingExclusions(/* isTimelineExcluded= */ true, /* areTracksExcluded= */ true), new BundlingExclusions(
/* isTimelineExcluded= */ true, /* areCurrentTracksExcluded= */ true),
availableCommands); availableCommands);
assertThat(mergeResult.first.timeline).isSameInstanceAs(oldPlayerInfo.timeline); assertThat(mergeResult.first.timeline).isSameInstanceAs(oldPlayerInfo.timeline);