Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.media;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.os.Bundle;
     24 import android.util.Log;
     25 
     26 import androidx.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
     27 import androidx.media.MediaSession2.ControllerInfo;
     28 import androidx.media.MediaSession2.SessionCallback;
     29 import androidx.media.TestUtils.SyncHandler;
     30 
     31 import java.util.ArrayList;
     32 import java.util.List;
     33 import java.util.concurrent.Executor;
     34 import java.util.concurrent.Executors;
     35 import java.util.concurrent.TimeUnit;
     36 
     37 import javax.annotation.concurrent.GuardedBy;
     38 
     39 /**
     40  * Mock implementation of {@link MediaLibraryService2} for testing.
     41  */
     42 public class MockMediaLibraryService2 extends MediaLibraryService2 {
     43     // Keep in sync with the AndroidManifest.xml
     44     public static final String ID = "TestLibrary";
     45 
     46     public static final String ROOT_ID = "rootId";
     47     public static final Bundle EXTRAS = new Bundle();
     48 
     49     public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
     50 
     51     public static final String PARENT_ID = "parent_id";
     52     public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
     53     public static final String PARENT_ID_ERROR = "parent_id_error";
     54 
     55     public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
     56     public static final int CHILDREN_COUNT = 100;
     57 
     58     public static final String SEARCH_QUERY = "search_query";
     59     public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
     60     public static final int SEARCH_TIME_IN_MS = 5000;
     61     public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
     62 
     63     public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
     64     public static final int SEARCH_RESULT_COUNT = 50;
     65 
     66     // TODO(jaewan): Uncomment here after DataSourceDesc.builder is ready.
     67 //    private static final DataSourceDesc DATA_SOURCE_DESC =
     68 //            new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
     69     private static final DataSourceDesc DATA_SOURCE_DESC = null;
     70 
     71     private static final String TAG = "MockMediaLibrarySvc2";
     72 
     73     static {
     74         EXTRAS.putString(ROOT_ID, ROOT_ID);
     75     }
     76     @GuardedBy("MockMediaLibraryService2.class")
     77     private static SessionToken2 sToken;
     78 
     79     private MediaLibrarySession mSession;
     80 
     81     public MockMediaLibraryService2() {
     82         super();
     83         GET_CHILDREN_RESULT.clear();
     84         String getChildrenMediaIdPrefix = "get_children_media_id_";
     85         for (int i = 0; i < CHILDREN_COUNT; i++) {
     86             GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
     87         }
     88 
     89         SEARCH_RESULT.clear();
     90         String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
     91         for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
     92             SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
     93         }
     94     }
     95 
     96     @Override
     97     public void onCreate() {
     98         TestServiceRegistry.getInstance().setServiceInstance(this);
     99         super.onCreate();
    100     }
    101 
    102     @Override
    103     public MediaLibrarySession onCreateSession(String sessionId) {
    104         final MockPlayer player = new MockPlayer(1);
    105         final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
    106         final Executor executor = new Executor() {
    107             @Override
    108             public void execute(Runnable runnable) {
    109                 handler.post(runnable);
    110             }
    111         };
    112         SessionCallback callback = TestServiceRegistry.getInstance().getSessionCallback();
    113         MediaLibrarySessionCallback librarySessionCallback;
    114         if (callback instanceof MediaLibrarySessionCallback) {
    115             librarySessionCallback = (MediaLibrarySessionCallback) callback;
    116         } else {
    117             // Callback hasn't set. Use default callback
    118             librarySessionCallback = new TestLibrarySessionCallback();
    119         }
    120         mSession = new MediaLibrarySession.Builder(MockMediaLibraryService2.this, executor,
    121                 librarySessionCallback).setPlayer(player).setId(sessionId).build();
    122         return mSession;
    123     }
    124 
    125     @Override
    126     public void onDestroy() {
    127         super.onDestroy();
    128         TestServiceRegistry.getInstance().cleanUp();
    129     }
    130 
    131     public static SessionToken2 getToken(Context context) {
    132         synchronized (MockMediaLibraryService2.class) {
    133             if (sToken == null) {
    134                 sToken = new SessionToken2(context, new ComponentName(
    135                         context.getPackageName(), MockMediaLibraryService2.class.getName()));
    136                 assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, sToken.getType());
    137             }
    138             return sToken;
    139         }
    140     }
    141 
    142     private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
    143         @Override
    144         public LibraryRoot onGetLibraryRoot(MediaLibrarySession session, ControllerInfo controller,
    145                 Bundle rootHints) {
    146             return new LibraryRoot(ROOT_ID, EXTRAS);
    147         }
    148 
    149         @Override
    150         public MediaItem2 onGetItem(MediaLibrarySession session, ControllerInfo controller,
    151                 String mediaId) {
    152             if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
    153                 return createMediaItem(mediaId);
    154             } else {
    155                 return null;
    156             }
    157         }
    158 
    159         @Override
    160         public List<MediaItem2> onGetChildren(MediaLibrarySession session,
    161                 ControllerInfo controller, String parentId, int page, int pageSize, Bundle extras) {
    162             if (PARENT_ID.equals(parentId)) {
    163                 return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
    164             } else if (PARENT_ID_ERROR.equals(parentId)) {
    165                 return null;
    166             }
    167             // Includes the case of PARENT_ID_NO_CHILDREN.
    168             return new ArrayList<>();
    169         }
    170 
    171         @Override
    172         public void onSearch(MediaLibrarySession session, final ControllerInfo controllerInfo,
    173                 final String query, final Bundle extras) {
    174             if (SEARCH_QUERY.equals(query)) {
    175                 mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
    176                         extras);
    177             } else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
    178                 // Searching takes some time. Notify after 5 seconds.
    179                 Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
    180                     @Override
    181                     public void run() {
    182                         mSession.notifySearchResultChanged(
    183                                 controllerInfo, query, SEARCH_RESULT_COUNT, extras);
    184                     }
    185                 }, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
    186             } else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
    187                 mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
    188             } else {
    189                 // TODO: For the error case, how should we notify the browser?
    190             }
    191         }
    192 
    193         @Override
    194         public List<MediaItem2> onGetSearchResult(MediaLibrarySession session,
    195                 ControllerInfo controllerInfo, String query, int page, int pageSize,
    196                 Bundle extras) {
    197             if (SEARCH_QUERY.equals(query)) {
    198                 return getPaginatedResult(SEARCH_RESULT, page, pageSize);
    199             } else {
    200                 return null;
    201             }
    202         }
    203     }
    204 
    205     private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
    206         if (items == null) {
    207             return null;
    208         } else if (items.size() == 0) {
    209             return new ArrayList<>();
    210         }
    211 
    212         final int totalItemCount = items.size();
    213         int fromIndex = (page - 1) * pageSize;
    214         int toIndex = Math.min(page * pageSize, totalItemCount);
    215 
    216         List<MediaItem2> paginatedResult = new ArrayList<>();
    217         try {
    218             // The case of (fromIndex >= totalItemCount) will throw exception below.
    219             paginatedResult = items.subList(fromIndex, toIndex);
    220         } catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
    221             Log.d(TAG, "Result is empty for given pagination arguments: totalItemCount="
    222                     + totalItemCount + ", page=" + page + ", pageSize=" + pageSize, ex);
    223         }
    224         return paginatedResult;
    225     }
    226 
    227     private MediaItem2 createMediaItem(String mediaId) {
    228         Context context = MockMediaLibraryService2.this;
    229         return new MediaItem2.Builder(0 /* Flags */)
    230                 .setMediaId(mediaId)
    231                 .setDataSourceDesc(DATA_SOURCE_DESC)
    232                 .setMetadata(new MediaMetadata2.Builder()
    233                                 .putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
    234                                 .build())
    235                 .build();
    236     }
    237 }
    238