blob: 8f5d5bba55361701bbd855b66d92a504290caeba [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media;
import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import androidx.media.MediaLibraryService2.MediaLibrarySession.MediaLibrarySessionCallback;
import androidx.media.MediaSession2.ControllerInfo;
import androidx.media.MediaSession2.SessionCallback;
import androidx.media.TestUtils.SyncHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.GuardedBy;
/**
* Mock implementation of {@link MediaLibraryService2} for testing.
*/
public class MockMediaLibraryService2 extends MediaLibraryService2 {
// Keep in sync with the AndroidManifest.xml
public static final String ID = "TestLibrary";
public static final String ROOT_ID = "rootId";
public static final Bundle EXTRAS = new Bundle();
public static final String MEDIA_ID_GET_ITEM = "media_id_get_item";
public static final String PARENT_ID = "parent_id";
public static final String PARENT_ID_NO_CHILDREN = "parent_id_no_children";
public static final String PARENT_ID_ERROR = "parent_id_error";
public static final List<MediaItem2> GET_CHILDREN_RESULT = new ArrayList<>();
public static final int CHILDREN_COUNT = 100;
public static final String SEARCH_QUERY = "search_query";
public static final String SEARCH_QUERY_TAKES_TIME = "search_query_takes_time";
public static final int SEARCH_TIME_IN_MS = 5000;
public static final String SEARCH_QUERY_EMPTY_RESULT = "search_query_empty_result";
public static final List<MediaItem2> SEARCH_RESULT = new ArrayList<>();
public static final int SEARCH_RESULT_COUNT = 50;
// TODO(jaewan): Uncomment here after DataSourceDesc.builder is ready.
// private static final DataSourceDesc DATA_SOURCE_DESC =
// new DataSourceDesc.Builder().setDataSource(new FileDescriptor()).build();
private static final DataSourceDesc DATA_SOURCE_DESC = null;
private static final String TAG = "MockMediaLibrarySvc2";
static {
EXTRAS.putString(ROOT_ID, ROOT_ID);
}
@GuardedBy("MockMediaLibraryService2.class")
private static SessionToken2 sToken;
private MediaLibrarySession mSession;
public MockMediaLibraryService2() {
super();
GET_CHILDREN_RESULT.clear();
String getChildrenMediaIdPrefix = "get_children_media_id_";
for (int i = 0; i < CHILDREN_COUNT; i++) {
GET_CHILDREN_RESULT.add(createMediaItem(getChildrenMediaIdPrefix + i));
}
SEARCH_RESULT.clear();
String getSearchResultMediaIdPrefix = "get_search_result_media_id_";
for (int i = 0; i < SEARCH_RESULT_COUNT; i++) {
SEARCH_RESULT.add(createMediaItem(getSearchResultMediaIdPrefix + i));
}
}
@Override
public void onCreate() {
TestServiceRegistry.getInstance().setServiceInstance(this);
super.onCreate();
}
@Override
public MediaLibrarySession onCreateSession(String sessionId) {
final MockPlayer player = new MockPlayer(1);
final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
final Executor executor = new Executor() {
@Override
public void execute(Runnable runnable) {
handler.post(runnable);
}
};
SessionCallback callback = TestServiceRegistry.getInstance().getSessionCallback();
MediaLibrarySessionCallback librarySessionCallback;
if (callback instanceof MediaLibrarySessionCallback) {
librarySessionCallback = (MediaLibrarySessionCallback) callback;
} else {
// Callback hasn't set. Use default callback
librarySessionCallback = new TestLibrarySessionCallback();
}
mSession = new MediaLibrarySession.Builder(MockMediaLibraryService2.this, executor,
librarySessionCallback).setPlayer(player).setId(sessionId).build();
return mSession;
}
@Override
public void onDestroy() {
super.onDestroy();
TestServiceRegistry.getInstance().cleanUp();
}
public static SessionToken2 getToken(Context context) {
synchronized (MockMediaLibraryService2.class) {
if (sToken == null) {
sToken = new SessionToken2(context, new ComponentName(
context.getPackageName(), MockMediaLibraryService2.class.getName()));
assertEquals(SessionToken2.TYPE_LIBRARY_SERVICE, sToken.getType());
}
return sToken;
}
}
private class TestLibrarySessionCallback extends MediaLibrarySessionCallback {
@Override
public LibraryRoot onGetLibraryRoot(MediaLibrarySession session, ControllerInfo controller,
Bundle rootHints) {
return new LibraryRoot(ROOT_ID, EXTRAS);
}
@Override
public MediaItem2 onGetItem(MediaLibrarySession session, ControllerInfo controller,
String mediaId) {
if (MEDIA_ID_GET_ITEM.equals(mediaId)) {
return createMediaItem(mediaId);
} else {
return null;
}
}
@Override
public List<MediaItem2> onGetChildren(MediaLibrarySession session,
ControllerInfo controller, String parentId, int page, int pageSize, Bundle extras) {
if (PARENT_ID.equals(parentId)) {
return getPaginatedResult(GET_CHILDREN_RESULT, page, pageSize);
} else if (PARENT_ID_ERROR.equals(parentId)) {
return null;
}
// Includes the case of PARENT_ID_NO_CHILDREN.
return new ArrayList<>();
}
@Override
public void onSearch(MediaLibrarySession session, final ControllerInfo controllerInfo,
final String query, final Bundle extras) {
if (SEARCH_QUERY.equals(query)) {
mSession.notifySearchResultChanged(controllerInfo, query, SEARCH_RESULT_COUNT,
extras);
} else if (SEARCH_QUERY_TAKES_TIME.equals(query)) {
// Searching takes some time. Notify after 5 seconds.
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
@Override
public void run() {
mSession.notifySearchResultChanged(
controllerInfo, query, SEARCH_RESULT_COUNT, extras);
}
}, SEARCH_TIME_IN_MS, TimeUnit.MILLISECONDS);
} else if (SEARCH_QUERY_EMPTY_RESULT.equals(query)) {
mSession.notifySearchResultChanged(controllerInfo, query, 0, extras);
} else {
// TODO: For the error case, how should we notify the browser?
}
}
@Override
public List<MediaItem2> onGetSearchResult(MediaLibrarySession session,
ControllerInfo controllerInfo, String query, int page, int pageSize,
Bundle extras) {
if (SEARCH_QUERY.equals(query)) {
return getPaginatedResult(SEARCH_RESULT, page, pageSize);
} else {
return null;
}
}
}
private List<MediaItem2> getPaginatedResult(List<MediaItem2> items, int page, int pageSize) {
if (items == null) {
return null;
} else if (items.size() == 0) {
return new ArrayList<>();
}
final int totalItemCount = items.size();
int fromIndex = (page - 1) * pageSize;
int toIndex = Math.min(page * pageSize, totalItemCount);
List<MediaItem2> paginatedResult = new ArrayList<>();
try {
// The case of (fromIndex >= totalItemCount) will throw exception below.
paginatedResult = items.subList(fromIndex, toIndex);
} catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
Log.d(TAG, "Result is empty for given pagination arguments: totalItemCount="
+ totalItemCount + ", page=" + page + ", pageSize=" + pageSize, ex);
}
return paginatedResult;
}
private MediaItem2 createMediaItem(String mediaId) {
Context context = MockMediaLibraryService2.this;
return new MediaItem2.Builder(0 /* Flags */)
.setMediaId(mediaId)
.setDataSourceDesc(DATA_SOURCE_DESC)
.setMetadata(new MediaMetadata2.Builder()
.putString(MediaMetadata2.METADATA_KEY_MEDIA_ID, mediaId)
.build())
.build();
}
}