1515 */
1616package androidx .media3 .session ;
1717
18+ import static androidx .media3 .common .MediaMetadata .PICTURE_TYPE_MEDIA ;
19+ import static androidx .media3 .test .utils .TestUtil .assertSubclassOverridesAllMethods ;
1820import static com .google .common .truth .Truth .assertThat ;
1921import static java .util .concurrent .TimeUnit .SECONDS ;
2022import static org .junit .Assert .assertThrows ;
2527import android .graphics .BitmapFactory ;
2628import android .graphics .Matrix ;
2729import android .net .Uri ;
30+ import androidx .media3 .common .MediaMetadata ;
2831import androidx .media3 .common .ParserException ;
32+ import androidx .media3 .common .util .BitmapLoader ;
2933import androidx .media3 .datasource .DataSourceBitmapLoader ;
3034import androidx .media3 .datasource .HttpDataSource ;
3135import androidx .media3 .test .utils .TestUtil ;
@@ -67,7 +71,12 @@ public void setUp() {
6771 }
6872
6973 @ Test
70- public void decodeBitmap_requestWithSameDataTwice_success () throws Exception {
74+ public void overridesAllMethods () throws NoSuchMethodException {
75+ assertSubclassOverridesAllMethods (BitmapLoader .class , CacheBitmapLoader .class );
76+ }
77+
78+ @ Test
79+ public void decodeBitmap_requestWithSameDataTwice_returnsCachedFuture () throws Exception {
7180 CacheBitmapLoader cacheBitmapLoader =
7281 new CacheBitmapLoader (new DataSourceBitmapLoader (context ));
7382 byte [] imageData = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
@@ -87,7 +96,7 @@ public void decodeBitmap_requestWithSameDataTwice_success() throws Exception {
8796 }
8897
8998 @ Test
90- public void decodeBitmap_requestWithDifferentData_success () throws Exception {
99+ public void decodeBitmap_requestWithDifferentData_returnsNewFuture () throws Exception {
91100 CacheBitmapLoader cacheBitmapLoader =
92101 new CacheBitmapLoader (new DataSourceBitmapLoader (context ));
93102 byte [] imageData1 = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
@@ -130,7 +139,7 @@ public void decodeBitmap_requestWithSameDataTwice_throwsException() {
130139 }
131140
132141 @ Test
133- public void loadBitmap_httpUri_requestWithSameUriTwice_success () throws Exception {
142+ public void loadBitmap_httpUri_requestWithSameUriTwice_returnsCachedFuture () throws Exception {
134143 CacheBitmapLoader cacheBitmapLoader =
135144 new CacheBitmapLoader (new DataSourceBitmapLoader (context ));
136145 byte [] imageData = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
@@ -155,7 +164,7 @@ public void loadBitmap_httpUri_requestWithSameUriTwice_success() throws Exceptio
155164 }
156165
157166 @ Test
158- public void loadBitmap_httpUri_requestWithDifferentUri_success () throws Exception {
167+ public void loadBitmap_httpUri_requestWithDifferentUri_returnsNewFuture () throws Exception {
159168 CacheBitmapLoader cacheBitmapLoader =
160169 new CacheBitmapLoader (new DataSourceBitmapLoader (context ));
161170 byte [] imageData1 = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
@@ -213,6 +222,173 @@ public void loadBitmap_httpUri_requestWithSameUriTwice_throwsException() {
213222 assertThat (mockWebServer .getRequestCount ()).isEqualTo (1 );
214223 }
215224
225+ @ Test
226+ public void loadBitmapFromMetadata_requestWithSameDataTwice_returnsCachedFuture ()
227+ throws Exception {
228+ CacheBitmapLoader cacheBitmapLoader =
229+ new CacheBitmapLoader (
230+ new LoadBitmapFromMetadataOnlyBitmapLoader (new DataSourceBitmapLoader (context )));
231+ byte [] imageData = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
232+ Bitmap expectedBitmap =
233+ apply90DegreeExifRotation (
234+ BitmapFactory .decodeByteArray (imageData , /* offset= */ 0 , imageData .length ));
235+ MediaMetadata metadata =
236+ new MediaMetadata .Builder ().setArtworkData (imageData , PICTURE_TYPE_MEDIA ).build ();
237+
238+ // First request, no cached bitmap load request.
239+ ListenableFuture <Bitmap > future1 = cacheBitmapLoader .loadBitmapFromMetadata (metadata );
240+
241+ assertThat (future1 .get (10 , SECONDS ).sameAs (expectedBitmap )).isTrue ();
242+
243+ // Second request, has cached bitmap load request.
244+ ListenableFuture <Bitmap > future2 = cacheBitmapLoader .loadBitmapFromMetadata (metadata );
245+
246+ assertThat (future1 ).isSameInstanceAs (future2 );
247+ }
248+
249+ @ Test
250+ public void loadBitmapFromMetadata_requestWithDifferentData_returnsNewFuture () throws Exception {
251+ CacheBitmapLoader cacheBitmapLoader =
252+ new CacheBitmapLoader (
253+ new LoadBitmapFromMetadataOnlyBitmapLoader (new DataSourceBitmapLoader (context )));
254+ byte [] imageData1 = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
255+ byte [] imageData2 = TestUtil .getByteArray (context , SECOND_TEST_IMAGE_PATH );
256+ Bitmap expectedBitmap1 =
257+ apply90DegreeExifRotation (
258+ BitmapFactory .decodeByteArray (imageData1 , /* offset= */ 0 , imageData1 .length ));
259+ Bitmap expectedBitmap2 =
260+ apply90DegreeExifRotation (
261+ BitmapFactory .decodeByteArray (imageData2 , /* offset= */ 0 , imageData2 .length ));
262+ MediaMetadata metadata1 =
263+ new MediaMetadata .Builder ().setArtworkData (imageData1 , PICTURE_TYPE_MEDIA ).build ();
264+ MediaMetadata metadata2 =
265+ new MediaMetadata .Builder ().setArtworkData (imageData2 , PICTURE_TYPE_MEDIA ).build ();
266+
267+ // First request.
268+ ListenableFuture <Bitmap > future1 = cacheBitmapLoader .loadBitmapFromMetadata (metadata1 );
269+
270+ assertThat (future1 .get (10 , SECONDS ).sameAs (expectedBitmap1 )).isTrue ();
271+
272+ // Second request.
273+ ListenableFuture <Bitmap > future2 = cacheBitmapLoader .loadBitmapFromMetadata (metadata2 );
274+
275+ assertThat (future2 .get (10 , SECONDS ).sameAs (expectedBitmap2 )).isTrue ();
276+ assertThat (future1 ).isNotSameInstanceAs (future2 );
277+ }
278+
279+ @ Test
280+ public void loadBitmapFromMetadata_requestWithSameDataTwice_throwsException () {
281+ CacheBitmapLoader cacheBitmapLoader =
282+ new CacheBitmapLoader (
283+ new LoadBitmapFromMetadataOnlyBitmapLoader (new DataSourceBitmapLoader (context )));
284+ MediaMetadata metadata =
285+ new MediaMetadata .Builder ().setArtworkData (new byte [0 ], PICTURE_TYPE_MEDIA ).build ();
286+
287+ // First request, no cached bitmap load request.
288+ ListenableFuture <Bitmap > future1 = cacheBitmapLoader .loadBitmapFromMetadata (metadata );
289+
290+ // Second request, has cached bitmap load request.
291+ ListenableFuture <Bitmap > future2 = cacheBitmapLoader .loadBitmapFromMetadata (metadata );
292+
293+ assertThat (future1 ).isSameInstanceAs (future2 );
294+ ExecutionException executionException =
295+ assertThrows (ExecutionException .class , () -> future1 .get (10 , SECONDS ));
296+ assertThat (executionException ).hasCauseThat ().isInstanceOf (ParserException .class );
297+ assertThat (executionException ).hasMessageThat ().contains ("Could not decode image data" );
298+ }
299+
300+ @ Test
301+ public void loadBitmapFromMetadata_requestWithSameUriTwice_returnsCachedFuture ()
302+ throws Exception {
303+ CacheBitmapLoader cacheBitmapLoader =
304+ new CacheBitmapLoader (
305+ new LoadBitmapFromMetadataOnlyBitmapLoader (new DataSourceBitmapLoader (context )));
306+ byte [] imageData = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
307+ Buffer responseBody = new Buffer ().write (imageData );
308+ MockWebServer mockWebServer = new MockWebServer ();
309+ mockWebServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (responseBody ));
310+ Uri uri = Uri .parse (mockWebServer .url ("test_path" ).toString ());
311+ Bitmap expectedBitmap =
312+ apply90DegreeExifRotation (
313+ BitmapFactory .decodeByteArray (imageData , /* offset= */ 0 , imageData .length ));
314+ MediaMetadata metadata = new MediaMetadata .Builder ().setArtworkUri (uri ).build ();
315+
316+ // First request, no cached bitmap load request.
317+ Bitmap bitmap = cacheBitmapLoader .loadBitmapFromMetadata (metadata ).get (10 , SECONDS );
318+
319+ assertThat (bitmap .sameAs (expectedBitmap )).isTrue ();
320+
321+ // Second request, has cached bitmap load request.
322+ bitmap = cacheBitmapLoader .loadBitmapFromMetadata (metadata ).get (10 , SECONDS );
323+
324+ assertThat (bitmap .sameAs (expectedBitmap )).isTrue ();
325+ assertThat (mockWebServer .getRequestCount ()).isEqualTo (1 );
326+ }
327+
328+ @ Test
329+ public void loadBitmapFromMetadata_requestWithDifferentUri_returnsNewFuture () throws Exception {
330+ CacheBitmapLoader cacheBitmapLoader =
331+ new CacheBitmapLoader (
332+ new LoadBitmapFromMetadataOnlyBitmapLoader (new DataSourceBitmapLoader (context )));
333+ byte [] imageData1 = TestUtil .getByteArray (context , TEST_IMAGE_PATH );
334+ byte [] imageData2 = TestUtil .getByteArray (context , SECOND_TEST_IMAGE_PATH );
335+ Buffer responseBody1 = new Buffer ().write (imageData1 );
336+ Buffer responseBody2 = new Buffer ().write (imageData2 );
337+ MockWebServer mockWebServer = new MockWebServer ();
338+ mockWebServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (responseBody1 ));
339+ mockWebServer .enqueue (new MockResponse ().setResponseCode (200 ).setBody (responseBody2 ));
340+ Uri uri1 = Uri .parse (mockWebServer .url ("test_path_1" ).toString ());
341+ Uri uri2 = Uri .parse (mockWebServer .url ("test_path_2" ).toString ());
342+ Bitmap expectedBitmap1 =
343+ apply90DegreeExifRotation (
344+ BitmapFactory .decodeByteArray (imageData1 , /* offset= */ 0 , imageData1 .length ));
345+ Bitmap expectedBitmap2 =
346+ apply90DegreeExifRotation (
347+ BitmapFactory .decodeByteArray (imageData2 , /* offset= */ 0 , imageData2 .length ));
348+ MediaMetadata metadata1 = new MediaMetadata .Builder ().setArtworkUri (uri1 ).build ();
349+ MediaMetadata metadata2 = new MediaMetadata .Builder ().setArtworkUri (uri2 ).build ();
350+
351+ // First request.
352+ Bitmap bitmap = cacheBitmapLoader .loadBitmapFromMetadata (metadata1 ).get (10 , SECONDS );
353+
354+ assertThat (bitmap .sameAs (expectedBitmap1 )).isTrue ();
355+
356+ // Second request.
357+ bitmap = cacheBitmapLoader .loadBitmapFromMetadata (metadata2 ).get (10 , SECONDS );
358+
359+ assertThat (bitmap .sameAs (expectedBitmap2 )).isTrue ();
360+ assertThat (mockWebServer .getRequestCount ()).isEqualTo (2 );
361+ }
362+
363+ @ Test
364+ public void loadBitmapFromMetadata_requestWithSameUriTwice_throwsException () {
365+ CacheBitmapLoader cacheBitmapLoader =
366+ new CacheBitmapLoader (
367+ new LoadBitmapFromMetadataOnlyBitmapLoader (new DataSourceBitmapLoader (context )));
368+ MockWebServer mockWebServer = new MockWebServer ();
369+ mockWebServer .enqueue (new MockResponse ().setResponseCode (404 ));
370+ Uri uri = Uri .parse (mockWebServer .url ("test_path" ).toString ());
371+ MediaMetadata metadata = new MediaMetadata .Builder ().setArtworkUri (uri ).build ();
372+
373+ // First request, no cached bitmap load request.
374+ ListenableFuture <Bitmap > future1 = cacheBitmapLoader .loadBitmapFromMetadata (metadata );
375+
376+ // Second request, has cached bitmap load request.
377+ ListenableFuture <Bitmap > future2 = cacheBitmapLoader .loadBitmapFromMetadata (metadata );
378+
379+ ExecutionException executionException1 =
380+ assertThrows (ExecutionException .class , () -> future1 .get (10 , SECONDS ));
381+ ExecutionException executionException2 =
382+ assertThrows (ExecutionException .class , () -> future2 .get (10 , SECONDS ));
383+ assertThat (executionException1 )
384+ .hasCauseThat ()
385+ .isInstanceOf (HttpDataSource .InvalidResponseCodeException .class );
386+ assertThat (executionException2 )
387+ .hasCauseThat ()
388+ .isInstanceOf (HttpDataSource .InvalidResponseCodeException .class );
389+ assertThat (mockWebServer .getRequestCount ()).isEqualTo (1 );
390+ }
391+
216392 private static Bitmap apply90DegreeExifRotation (Bitmap bitmap ) {
217393 Matrix rotationMatrix = new Matrix ();
218394 rotationMatrix .postRotate (/* degrees= */ 90 );
@@ -225,4 +401,32 @@ private static Bitmap apply90DegreeExifRotation(Bitmap bitmap) {
225401 rotationMatrix ,
226402 /* filter= */ false );
227403 }
404+
405+ private static class LoadBitmapFromMetadataOnlyBitmapLoader implements BitmapLoader {
406+ private final BitmapLoader bitmapLoader ;
407+
408+ private LoadBitmapFromMetadataOnlyBitmapLoader (BitmapLoader bitmapLoader ) {
409+ this .bitmapLoader = bitmapLoader ;
410+ }
411+
412+ @ Override
413+ public boolean supportsMimeType (String mimeType ) {
414+ return true ;
415+ }
416+
417+ @ Override
418+ public ListenableFuture <Bitmap > decodeBitmap (byte [] data ) {
419+ throw new UnsupportedOperationException ();
420+ }
421+
422+ @ Override
423+ public ListenableFuture <Bitmap > loadBitmap (Uri uri ) {
424+ throw new UnsupportedOperationException ();
425+ }
426+
427+ @ Override
428+ public ListenableFuture <Bitmap > loadBitmapFromMetadata (MediaMetadata metadata ) {
429+ return bitmapLoader .loadBitmapFromMetadata (metadata );
430+ }
431+ }
228432}
0 commit comments