Merge "Revert "Revert "Move MediaBufferXXX from foundation to libmediaextractor"""
diff --git a/packages/MediaComponents/res/layout/media_controller.xml b/packages/MediaComponents/res/layout/media_controller.xml
index 4d05546..74a8306 100644
--- a/packages/MediaComponents/res/layout/media_controller.xml
+++ b/packages/MediaComponents/res/layout/media_controller.xml
@@ -136,7 +136,13 @@
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:visibility="gone"
- android:orientation="horizontal" >
+ android:orientation="horizontal"
+ android:gravity="center">
+
+ <LinearLayout
+ android:id="@+id/custom_buttons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
<ImageButton
android:id="@+id/mute"
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index 43f8473..6567cd0 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -222,38 +222,38 @@
@Override
public void play_impl() {
- sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_START);
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_START);
}
@Override
public void pause_impl() {
- sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE);
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE);
}
@Override
public void stop_impl() {
- sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_STOP);
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_STOP);
}
@Override
public void skipToPrevious_impl() {
- sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
}
@Override
public void skipToNext_impl() {
- sendCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
}
- private void sendCommand(int code) {
- // TODO(jaewan): optimization) Cache Command objects?
- Command command = new Command(code);
- // TODO(jaewan): Check if the command is in the allowed group.
+ private void sendTransportControlCommand(int commandCode) {
+ sendTransportControlCommand(commandCode, 0);
+ }
+ private void sendTransportControlCommand(int commandCode, long arg) {
final IMediaSession2 binder = mSessionBinder;
if (binder != null) {
try {
- binder.sendCommand(mSessionCallbackStub, command.toBundle(), null);
+ binder.sendTransportControlCommand(mSessionCallbackStub, commandCode, arg);
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to the service or the session is gone", e);
}
@@ -341,27 +341,28 @@
@Override
public void prepare_impl() {
- // TODO(jaewan): Implement
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE);
}
@Override
public void fastForward_impl() {
- // TODO(jaewan): Implement
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD);
}
@Override
public void rewind_impl() {
- // TODO(jaewan): Implement
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_REWIND);
}
@Override
public void seekTo_impl(long pos) {
- // TODO(jaewan): Implement
+ sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO, pos);
}
@Override
public void setCurrentPlaylistItem_impl(int index) {
- // TODO(jaewan): Implement
+ sendTransportControlCommand(
+ MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM, index);
}
@Override
diff --git a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
index bbb3411..d713f78 100644
--- a/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java
@@ -22,10 +22,9 @@
import android.media.MediaLibraryService2;
import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
-import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.VolumeProvider;
import android.media.update.MediaLibraryService2Provider;
@@ -68,8 +67,8 @@
private final MediaLibrarySessionCallback mCallback;
public MediaLibrarySessionImpl(Context context, MediaLibrarySession instance,
- MediaPlayerBase player, String id, VolumeProvider volumeProvider, int ratingType,
- PendingIntent sessionActivity, Executor callbackExecutor,
+ MediaPlayerInterface player, String id, VolumeProvider volumeProvider,
+ int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
MediaLibrarySessionCallback callback) {
super(context, instance, player, id, volumeProvider, ratingType, sessionActivity,
callbackExecutor, callback);
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 22a3187..28834a3 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -24,7 +24,7 @@
import android.media.AudioAttributes;
import android.media.IMediaSession2Callback;
import android.media.MediaItem2;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.Command;
@@ -39,11 +39,11 @@
import android.media.session.MediaSessionManager;
import android.media.update.MediaSession2Provider;
import android.os.Bundle;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
import android.os.ResultReceiver;
+import android.support.annotation.GuardedBy;
import android.util.Log;
+
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -53,20 +53,21 @@
private static final String TAG = "MediaSession2";
private static final boolean DEBUG = true;//Log.isLoggable(TAG, Log.DEBUG);
- private final MediaSession2 mInstance;
+ private final Object mLock = new Object();
+ private final MediaSession2 mInstance;
private final Context mContext;
private final String mId;
- private final Handler mHandler;
private final Executor mCallbackExecutor;
+ private final SessionCallback mCallback;
private final MediaSession2Stub mSessionStub;
private final SessionToken2 mSessionToken;
-
- private MediaPlayerBase mPlayer;
-
private final List<PlaybackListenerHolder> mListeners = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private MediaPlayerInterface mPlayer;
+ @GuardedBy("mLock")
private MyPlaybackListener mListener;
- private MediaSession2 instance;
/**
* Can be only called by the {@link Builder#build()}.
@@ -80,7 +81,7 @@
* @param ratingType
* @param sessionActivity
*/
- public MediaSession2Impl(Context context, MediaSession2 instance, MediaPlayerBase player,
+ public MediaSession2Impl(Context context, MediaSession2 instance, MediaPlayerInterface player,
String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
Executor callbackExecutor, SessionCallback callback) {
mInstance = instance;
@@ -90,9 +91,9 @@
// Initialize finals first.
mContext = context;
mId = id;
- mHandler = new Handler(Looper.myLooper());
+ mCallback = callback;
mCallbackExecutor = callbackExecutor;
- mSessionStub = new MediaSession2Stub(this, callback);
+ mSessionStub = new MediaSession2Stub(this);
// Ask server to create session token for following reasons.
// 1. Make session ID unique per package.
// Server can only know if the package has another process and has another session
@@ -118,7 +119,8 @@
// setPlayer(null). Token can be available when player is null, and
// controller can also attach to session.
@Override
- public void setPlayer_impl(MediaPlayerBase player, VolumeProvider volumeProvider) throws IllegalArgumentException {
+ public void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider)
+ throws IllegalArgumentException {
ensureCallingThread();
if (player == null) {
throw new IllegalArgumentException("player shouldn't be null");
@@ -126,27 +128,25 @@
setPlayerInternal(player);
}
- private void setPlayerInternal(MediaPlayerBase player) {
- if (mPlayer == player) {
- // Player didn't changed. No-op.
- return;
+ private void setPlayerInternal(MediaPlayerInterface player) {
+ synchronized (mLock) {
+ if (mPlayer == player) {
+ // Player didn't changed. No-op.
+ return;
+ }
+ if (mPlayer != null && mListener != null) {
+ // This might not work for a poorly implemented player.
+ mPlayer.removePlaybackListener(mListener);
+ }
+ mListener = new MyPlaybackListener(this, player);
+ player.addPlaybackListener(mCallbackExecutor, mListener);
+ mPlayer = player;
}
- // TODO(jaewan): Find equivalent for the executor
- //mHandler.removeCallbacksAndMessages(null);
- if (mPlayer != null && mListener != null) {
- // This might not work for a poorly implemented player.
- mPlayer.removePlaybackListener(mListener);
- }
- mListener = new MyPlaybackListener(this, player);
- player.addPlaybackListener(mCallbackExecutor, mListener);
- notifyPlaybackStateChanged(player.getPlaybackState());
- mPlayer = player;
+ notifyPlaybackStateChangedNotLocked(player.getPlaybackState());
}
@Override
public void close_impl() {
- // Flush any pending messages.
- mHandler.removeCallbacksAndMessages(null);
if (mSessionStub != null) {
if (DEBUG) {
Log.d(TAG, "session is now unavailable, id=" + mId);
@@ -154,10 +154,18 @@
// Invalidate previously published session stub.
mSessionStub.destroyNotLocked();
}
+ synchronized (mLock) {
+ if (mPlayer != null) {
+ // close can be called multiple times
+ mPlayer.removePlaybackListener(mListener);
+ mPlayer = null;
+ return;
+ }
+ }
}
@Override
- public MediaPlayerBase getPlayer_impl() {
+ public MediaPlayerInterface getPlayer_impl() {
return getPlayer();
}
@@ -233,7 +241,7 @@
// TODO(jaewan): Implement follows
//////////////////////////////////////////////////////////////////////////////////////
@Override
- public void setPlayer_impl(MediaPlayerBase player) {
+ public void setPlayer_impl(MediaPlayerInterface player) {
// TODO(jaewan): Implement
}
@@ -265,27 +273,37 @@
@Override
public void prepare_impl() {
- // TODO(jaewan): Implement
+ ensureCallingThread();
+ ensurePlayer();
+ mPlayer.prepare();
}
@Override
public void fastForward_impl() {
- // TODO(jaewan): Implement
+ ensureCallingThread();
+ ensurePlayer();
+ mPlayer.fastForward();
}
@Override
public void rewind_impl() {
- // TODO(jaewan): Implement
+ ensureCallingThread();
+ ensurePlayer();
+ mPlayer.rewind();
}
@Override
public void seekTo_impl(long pos) {
- // TODO(jaewan): Implement
+ ensureCallingThread();
+ ensurePlayer();
+ mPlayer.seekTo(pos);
}
@Override
public void setCurrentPlaylistItem_impl(int index) {
- // TODO(jaewan): Implement
+ ensureCallingThread();
+ ensurePlayer();
+ mPlayer.setCurrentPlaylistItem(index);
}
///////////////////////////////////////////////////
@@ -321,14 +339,14 @@
}
}
- Handler getHandler() {
- return mHandler;
- }
-
- private void notifyPlaybackStateChanged(PlaybackState2 state) {
+ private void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) {
+ List<PlaybackListenerHolder> listeners = new ArrayList<>();
+ synchronized (mLock) {
+ listeners.addAll(mListeners);
+ }
// Notify to listeners added directly to this session
- for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).postPlaybackChange(state);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).postPlaybackChange(state);
}
// Notify to controllers as well.
mSessionStub.notifyPlaybackStateChangedNotLocked(state);
@@ -342,15 +360,23 @@
return mInstance;
}
- MediaPlayerBase getPlayer() {
+ MediaPlayerInterface getPlayer() {
return mPlayer;
}
- private static class MyPlaybackListener implements MediaPlayerBase.PlaybackListener {
- private final WeakReference<MediaSession2Impl> mSession;
- private final MediaPlayerBase mPlayer;
+ Executor getCallbackExecutor() {
+ return mCallbackExecutor;
+ }
- private MyPlaybackListener(MediaSession2Impl session, MediaPlayerBase player) {
+ SessionCallback getCallback() {
+ return mCallback;
+ }
+
+ private static class MyPlaybackListener implements MediaPlayerInterface.PlaybackListener {
+ private final WeakReference<MediaSession2Impl> mSession;
+ private final MediaPlayerInterface mPlayer;
+
+ private MyPlaybackListener(MediaSession2Impl session, MediaPlayerInterface player) {
mSession = new WeakReference<>(session);
mPlayer = player;
}
@@ -363,7 +389,7 @@
new IllegalStateException());
return;
}
- session.notifyPlaybackStateChanged(state);
+ session.notifyPlaybackStateChangedNotLocked(state);
}
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 2f75dfa..69e8498 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -51,30 +51,19 @@
private static final boolean DEBUG = true; // TODO(jaewan): Rename.
private final Object mLock = new Object();
- private final CommandHandler mCommandHandler;
private final WeakReference<MediaSession2Impl> mSession;
- private final Context mContext;
- private final SessionCallback mSessionCallback;
- private final MediaLibrarySessionCallback mLibraryCallback;
@GuardedBy("mLock")
private final ArrayMap<IBinder, ControllerInfo> mControllers = new ArrayMap<>();
- public MediaSession2Stub(MediaSession2Impl session, SessionCallback callback) {
+ public MediaSession2Stub(MediaSession2Impl session) {
mSession = new WeakReference<>(session);
- mContext = session.getContext();
- // TODO(jaewan): Should be executor from the session builder
- mCommandHandler = new CommandHandler(session.getHandler().getLooper());
- mSessionCallback = callback;
- mLibraryCallback = (callback instanceof MediaLibrarySessionCallback)
- ? (MediaLibrarySessionCallback) callback : null;
}
public void destroyNotLocked() {
final List<ControllerInfo> list;
synchronized (mLock) {
mSession.clear();
- mCommandHandler.removeCallbacksAndMessages(null);
list = getControllers();
mControllers.clear();
}
@@ -99,14 +88,43 @@
}
@Override
- public void connect(String callingPackage, IMediaSession2Callback callback) {
- if (callback == null) {
- // Requesting connect without callback to receive result.
- return;
- }
- ControllerInfo request = new ControllerInfo(mContext,
+ public void connect(String callingPackage, IMediaSession2Callback callback)
+ throws RuntimeException {
+ final MediaSession2Impl sessionImpl = getSession();
+ final ControllerInfo request = new ControllerInfo(sessionImpl.getContext(),
Binder.getCallingUid(), Binder.getCallingPid(), callingPackage, callback);
- mCommandHandler.postConnect(request);
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ CommandGroup allowedCommands = session.getCallback().onConnect(request);
+ // Don't reject connection for the request from trusted app.
+ // Otherwise server will fail to retrieve session's information to dispatch
+ // media keys to.
+ boolean accept = allowedCommands != null || request.isTrusted();
+ ControllerInfoImpl impl = ControllerInfoImpl.from(request);
+ if (accept) {
+ synchronized (mLock) {
+ mControllers.put(impl.getId(), request);
+ }
+ if (allowedCommands == null) {
+ // For trusted apps, send non-null allowed commands to keep connection.
+ allowedCommands = new CommandGroup();
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onConnectResult, request=" + request
+ + " accept=" + accept);
+ }
+ try {
+ impl.getControllerBinder().onConnectionChanged(
+ accept ? MediaSession2Stub.this : null,
+ allowedCommands == null ? null : allowedCommands.toBundle());
+ } catch (RemoteException e) {
+ // Controller may be died prematurely.
+ }
+ });
}
@Override
@@ -122,20 +140,79 @@
@Override
public void sendCommand(IMediaSession2Callback caller, Bundle command, Bundle args)
throws RuntimeException {
- ControllerInfo controller = getController(caller);
+ // TODO(jaewan): Generic command
+ }
+
+ @Override
+ public void sendTransportControlCommand(IMediaSession2Callback caller,
+ int commandCode, long arg) throws RuntimeException {
+ final MediaSession2Impl sessionImpl = getSession();
+ final ControllerInfo controller = getController(caller);
if (controller == null) {
if (DEBUG) {
Log.d(TAG, "Command from a controller that hasn't connected. Ignore");
}
return;
}
- mCommandHandler.postCommand(controller, Command.fromBundle(command), args);
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ // TODO(jaewan): Sanity check.
+ Command command = new Command(commandCode);
+ boolean accepted = session.getCallback().onCommandRequest(controller, command);
+ if (!accepted) {
+ // Don't run rejected command.
+ if (DEBUG) {
+ Log.d(TAG, "Command " + commandCode + " from "
+ + controller + " was rejected by " + session);
+ }
+ return;
+ }
+
+ switch (commandCode) {
+ case MediaSession2.COMMAND_CODE_PLAYBACK_START:
+ session.getInstance().play();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE:
+ session.getInstance().pause();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_STOP:
+ session.getInstance().stop();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
+ session.getInstance().skipToPrevious();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
+ session.getInstance().skipToNext();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE:
+ session.getInstance().prepare();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD:
+ session.getInstance().fastForward();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_REWIND:
+ session.getInstance().rewind();
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
+ session.getInstance().seekTo(arg);
+ break;
+ case MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM:
+ session.getInstance().setCurrentPlaylistItem((int) arg);
+ break;
+ default:
+ // TODO(jaewan): Resend unknown (new) commands through the custom command.
+ }
+ });
}
@Override
public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
throws RuntimeException {
- if (mLibraryCallback == null) {
+ final MediaSession2Impl sessionImpl = getSession();
+ if (!(sessionImpl.getCallback() instanceof MediaLibrarySessionCallback)) {
if (DEBUG) {
Log.d(TAG, "Session cannot hand getBrowserRoot()");
}
@@ -148,7 +225,24 @@
}
return;
}
- mCommandHandler.postOnGetRoot(controller, rootHints);
+ sessionImpl.getCallbackExecutor().execute(() -> {
+ final MediaSession2Impl session = mSession.get();
+ if (session == null) {
+ return;
+ }
+ final MediaLibrarySessionCallback libraryCallback =
+ (MediaLibrarySessionCallback) session.getCallback();
+ final ControllerInfoImpl controllerImpl = ControllerInfoImpl.from(controller);
+ BrowserRoot root = libraryCallback.onGetRoot(controller, rootHints);
+ try {
+ controllerImpl.getControllerBinder().onGetRootResult(rootHints,
+ root == null ? null : root.getRootId(),
+ root == null ? null : root.getExtras());
+ } catch (RemoteException e) {
+ // Controller may be died prematurely.
+ // TODO(jaewan): Handle this.
+ }
+ });
}
@Deprecated
@@ -250,131 +344,4 @@
// TODO(jaewan): What to do when the controller is gone?
}
}
-
- // TODO(jaewan): Remove this. We should use Executor given by the session builder.
- private class CommandHandler extends Handler {
- public static final int MSG_CONNECT = 1000;
- public static final int MSG_COMMAND = 1001;
- public static final int MSG_ON_GET_ROOT = 2000;
-
- public CommandHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- final MediaSession2Impl session = MediaSession2Stub.this.mSession.get();
- if (session == null || session.getPlayer() == null) {
- return;
- }
-
- switch (msg.what) {
- case MSG_CONNECT: {
- ControllerInfo request = (ControllerInfo) msg.obj;
- CommandGroup allowedCommands = mSessionCallback.onConnect(request);
- // Don't reject connection for the request from trusted app.
- // Otherwise server will fail to retrieve session's information to dispatch
- // media keys to.
- boolean accept = allowedCommands != null || request.isTrusted();
- ControllerInfoImpl impl = ControllerInfoImpl.from(request);
- if (accept) {
- synchronized (mLock) {
- mControllers.put(impl.getId(), request);
- }
- if (allowedCommands == null) {
- // For trusted apps, send non-null allowed commands to keep connection.
- allowedCommands = new CommandGroup();
- }
- }
- if (DEBUG) {
- Log.d(TAG, "onConnectResult, request=" + request
- + " accept=" + accept);
- }
- try {
- impl.getControllerBinder().onConnectionChanged(
- accept ? MediaSession2Stub.this : null,
- allowedCommands == null ? null : allowedCommands.toBundle());
- } catch (RemoteException e) {
- // Controller may be died prematurely.
- }
- break;
- }
- case MSG_COMMAND: {
- CommandParam param = (CommandParam) msg.obj;
- Command command = param.command;
- boolean accepted = mSessionCallback.onCommandRequest(
- param.controller, command);
- if (!accepted) {
- // Don't run rejected command.
- if (DEBUG) {
- Log.d(TAG, "Command " + command + " from "
- + param.controller + " was rejected by " + session);
- }
- return;
- }
-
- switch (param.command.getCommandCode()) {
- case MediaSession2.COMMAND_CODE_PLAYBACK_START:
- session.getInstance().play();
- break;
- case MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE:
- session.getInstance().pause();
- break;
- case MediaSession2.COMMAND_CODE_PLAYBACK_STOP:
- session.getInstance().stop();
- break;
- case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
- session.getInstance().skipToPrevious();
- break;
- case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
- session.getInstance().skipToNext();
- break;
- default:
- // TODO(jaewan): Handle custom command.
- }
- break;
- }
- case MSG_ON_GET_ROOT: {
- final CommandParam param = (CommandParam) msg.obj;
- final ControllerInfoImpl controller = ControllerInfoImpl.from(param.controller);
- BrowserRoot root = mLibraryCallback.onGetRoot(param.controller, param.args);
- try {
- controller.getControllerBinder().onGetRootResult(param.args,
- root == null ? null : root.getRootId(),
- root == null ? null : root.getExtras());
- } catch (RemoteException e) {
- // Controller may be died prematurely.
- // TODO(jaewan): Handle this.
- }
- break;
- }
- }
- }
-
- public void postConnect(ControllerInfo request) {
- obtainMessage(MSG_CONNECT, request).sendToTarget();
- }
-
- public void postCommand(ControllerInfo controller, Command command, Bundle args) {
- CommandParam param = new CommandParam(controller, command, args);
- obtainMessage(MSG_COMMAND, param).sendToTarget();
- }
-
- public void postOnGetRoot(ControllerInfo controller, Bundle rootHints) {
- CommandParam param = new CommandParam(controller, null, rootHints);
- obtainMessage(MSG_ON_GET_ROOT, param).sendToTarget();
- }
- }
-
- private static class CommandParam {
- public final ControllerInfo controller;
- public final Command command;
- public final Bundle args;
-
- private CommandParam(ControllerInfo controller, Command command, Bundle args) {
- this.controller = controller;
- this.command = command;
- this.args = args;
- }
- }
}
diff --git a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
index 9d24082..b9db305 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSessionService2Impl.java
@@ -22,7 +22,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2;
import android.media.MediaSessionService2;
import android.media.MediaSessionService2.MediaNotification;
@@ -30,7 +30,6 @@
import android.media.session.PlaybackState;
import android.media.update.MediaSessionService2Provider;
import android.os.IBinder;
-import android.os.Looper;
import android.support.annotation.GuardedBy;
import android.util.Log;
@@ -152,11 +151,6 @@
return;
}
MediaSession2Impl impl = (MediaSession2Impl) mSession.getProvider();
- if (impl.getHandler().getLooper() != Looper.myLooper()) {
- Log.w(TAG, "Ignoring " + state + ". Expected " + impl.getHandler().getLooper()
- + " but " + Looper.myLooper());
- return;
- }
updateNotification(state);
}
}
diff --git a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
index 7b336c4..4241f85 100644
--- a/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
+++ b/packages/MediaComponents/src/com/android/media/PlaybackListenerHolder.java
@@ -16,11 +16,9 @@
package com.android.media;
-import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.PlaybackState2;
-import android.media.session.PlaybackState;
import android.os.Handler;
-import android.os.Message;
import android.support.annotation.NonNull;
import java.util.List;
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 8ce7bef..bb67bcd 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -27,7 +27,7 @@
import android.media.MediaLibraryService2;
import android.media.MediaLibraryService2.MediaLibrarySession;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.SessionCallback;
@@ -83,7 +83,7 @@
@Override
public MediaSession2Provider createMediaSession2(Context context, MediaSession2 instance,
- MediaPlayerBase player, String id, VolumeProvider volumeProvider,
+ MediaPlayerInterface player, String id, VolumeProvider volumeProvider,
int ratingType, PendingIntent sessionActivity, Executor callbackExecutor,
SessionCallback callback) {
return new MediaSession2Impl(context, instance, player, id, volumeProvider, ratingType,
@@ -112,7 +112,7 @@
@Override
public MediaLibrarySessionProvider createMediaLibraryService2MediaLibrarySession(
- Context context, MediaLibrarySession instance, MediaPlayerBase player,
+ Context context, MediaLibrarySession instance, MediaPlayerInterface player,
String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity,
Executor callbackExecutor, MediaLibrarySessionCallback callback) {
return new MediaLibrarySessionImpl(context, instance, player, id, volumeProvider,
diff --git a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
index bc370d8..f96ff70 100644
--- a/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/MediaControlView2Impl.java
@@ -24,11 +24,11 @@
import android.media.update.ViewProvider;
import android.os.Bundle;
import android.util.Log;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
import android.widget.ImageButton;
import android.widget.MediaControlView2;
import android.widget.ProgressBar;
@@ -40,6 +40,7 @@
import com.android.media.update.R;
import java.util.Formatter;
+import java.util.List;
import java.util.Locale;
public class MediaControlView2Impl implements MediaControlView2Provider {
@@ -97,6 +98,7 @@
private ImageButton mOverflowButtonRight;
private ViewGroup mExtraControls;
+ private ViewGroup mCustomButtons;
private ImageButton mOverflowButtonLeft;
private ImageButton mMuteButton;
private ImageButton mAspectRationButton;
@@ -305,62 +307,11 @@
}
@Override
- public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
- return mSuperProvider.onKeyDown_impl(keyCode, event);
- }
-
- @Override
public void onFinishInflate_impl() {
mSuperProvider.onFinishInflate_impl();
}
@Override
- public boolean dispatchKeyEvent_impl(KeyEvent event) {
- int keyCode = event.getKeyCode();
- final boolean uniqueDown = event.getRepeatCount() == 0
- && event.getAction() == KeyEvent.ACTION_DOWN;
- if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
- || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
- || keyCode == KeyEvent.KEYCODE_SPACE) {
- if (uniqueDown) {
- togglePausePlayState();
- mInstance.show(DEFAULT_TIMEOUT_MS);
- if (mPlayPauseButton != null) {
- mPlayPauseButton.requestFocus();
- }
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
- if (uniqueDown && !isPlaying()) {
- togglePausePlayState();
- mInstance.show(DEFAULT_TIMEOUT_MS);
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
- || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
- if (uniqueDown && isPlaying()) {
- togglePausePlayState();
- mInstance.show(DEFAULT_TIMEOUT_MS);
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
- || keyCode == KeyEvent.KEYCODE_VOLUME_UP
- || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
- || keyCode == KeyEvent.KEYCODE_CAMERA) {
- // don't show the controls for volume adjustment
- return mSuperProvider.dispatchKeyEvent_impl(event);
- } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {
- if (uniqueDown) {
- mInstance.hide();
- }
- return true;
- }
-
- mInstance.show(DEFAULT_TIMEOUT_MS);
- return mSuperProvider.dispatchKeyEvent_impl(event);
- }
-
- @Override
public void setEnabled_impl(boolean enabled) {
if (mPlayPauseButton != null) {
mPlayPauseButton.setEnabled(enabled);
@@ -501,6 +452,7 @@
// TODO: should these buttons be shown as default?
mExtraControls = v.findViewById(R.id.extra_controls);
+ mCustomButtons = v.findViewById(R.id.custom_buttons);
mOverflowButtonLeft = v.findViewById(R.id.overflow_left);
if (mOverflowButtonLeft != null) {
mOverflowButtonLeft.setOnClickListener(mOverflowLeftListener);
@@ -875,6 +827,31 @@
}
mPlaybackActions = newActions;
}
+
+ // Add buttons if custom actions are present.
+ List<PlaybackState.CustomAction> customActions = mPlaybackState.getCustomActions();
+ mCustomButtons.removeAllViews();
+ if (customActions.size() > 0) {
+ for (PlaybackState.CustomAction action : customActions) {
+ ImageButton button = new ImageButton(mInstance.getContext(),
+ null /* AttributeSet */, 0 /* Style */);
+ // TODO: Apply R.style.BottomBarButton to this button using library context.
+ // Refer Constructor with argument (int defStyleRes) of View.java
+ button.setImageResource(action.getIcon());
+ button.setTooltipText(action.getName());
+ final String actionString = action.getAction().toString();
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // TODO: Currently, we are just sending extras that came from session.
+ // Is it the right behavior?
+ mControls.sendCustomAction(actionString, action.getExtras());
+ mInstance.show(DEFAULT_TIMEOUT_MS);
+ }
+ });
+ mCustomButtons.addView(button);
+ }
+ }
}
@Override
diff --git a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
index 09f3cda..8cd932d 100644
--- a/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
+++ b/packages/MediaComponents/src/com/android/widget/VideoView2Impl.java
@@ -25,11 +25,9 @@
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.MediaPlayer;
-import android.media.MediaPlayerBase;
+import android.media.MediaPlayerInterface;
import android.media.Cea708CaptionRenderer;
import android.media.ClosedCaptionRenderer;
-import android.media.MediaMetadata;
-import android.media.MediaPlayer;
import android.media.Metadata;
import android.media.PlaybackParams;
import android.media.SubtitleController;
@@ -47,7 +45,6 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
-import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout.LayoutParams;
@@ -80,8 +77,8 @@
private final AudioManager mAudioManager;
private AudioAttributes mAudioAttributes;
private int mAudioFocusType = AudioManager.AUDIOFOCUS_GAIN; // legacy focus gain
- private int mAudioSession;
+ private VideoView2.OnCustomActionListener mOnCustomActionListener;
private VideoView2.OnPreparedListener mOnPreparedListener;
private VideoView2.OnCompletionListener mOnCompletionListener;
private VideoView2.OnErrorListener mOnErrorListener;
@@ -101,6 +98,7 @@
private String mTitle;
private PlaybackState.Builder mStateBuilder;
+ private List<PlaybackState.CustomAction> mCustomActionList;
private int mTargetState = STATE_IDLE;
private int mCurrentState = STATE_IDLE;
private int mCurrentBufferPercentage;
@@ -194,16 +192,6 @@
}
@Override
- public int getAudioSessionId_impl() {
- if (mAudioSession == 0) {
- MediaPlayer foo = new MediaPlayer();
- mAudioSession = foo.getAudioSessionId();
- foo.release();
- }
- return mAudioSession;
- }
-
- @Override
public void showSubtitle_impl() {
// Retrieve all tracks that belong to the current video.
MediaPlayer.TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
@@ -234,7 +222,7 @@
@Override
public void setFullScreen_impl(boolean fullScreen) {
if (mOnFullScreenChangedListener != null) {
- mOnFullScreenChangedListener.onFullScreenChanged(fullScreen);
+ mOnFullScreenChangedListener.onFullScreenChanged(mInstance, fullScreen);
}
}
@@ -273,22 +261,22 @@
}
@Override
- public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) {
+ public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerInterface player) {
// TODO: implement this.
}
@Override
public void setVideoPath_impl(String path) {
- mInstance.setVideoURI(Uri.parse(path));
+ mInstance.setVideoUri(Uri.parse(path));
}
@Override
- public void setVideoURI_impl(Uri uri) {
- mInstance.setVideoURI(uri, null);
+ public void setVideoUri_impl(Uri uri) {
+ mInstance.setVideoUri(uri, null);
}
@Override
- public void setVideoURI_impl(Uri uri, Map<String, String> headers) {
+ public void setVideoUri_impl(Uri uri, Map<String, String> headers) {
mSeekWhenPrepared = 0;
openVideo(uri, headers);
}
@@ -319,6 +307,17 @@
}
@Override
+ public void setCustomActions_impl(List<PlaybackState.CustomAction> actionList,
+ VideoView2.OnCustomActionListener listener) {
+ mCustomActionList = actionList;
+ mOnCustomActionListener = listener;
+
+ // Create a new playback builder in order to clear existing the custom actions.
+ mStateBuilder = null;
+ updatePlaybackState();
+ }
+
+ @Override
public void setOnPreparedListener_impl(VideoView2.OnPreparedListener l) {
mOnPreparedListener = l;
}
@@ -365,6 +364,7 @@
mSuperProvider.onDetachedFromWindow_impl();
mMediaSession.release();
mMediaSession = null;
+ mMediaController = null;
}
@Override
@@ -395,58 +395,11 @@
}
@Override
- public boolean onKeyDown_impl(int keyCode, KeyEvent event) {
- Log.v(TAG, "onKeyDown_impl: " + keyCode);
- boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK
- && keyCode != KeyEvent.KEYCODE_VOLUME_UP
- && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN
- && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE
- && keyCode != KeyEvent.KEYCODE_MENU
- && keyCode != KeyEvent.KEYCODE_CALL
- && keyCode != KeyEvent.KEYCODE_ENDCALL;
- if (isInPlaybackState() && isKeyCodeSupported && mMediaControlView != null) {
- if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK
- || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
- if (mMediaPlayer.isPlaying()) {
- mMediaController.getTransportControls().pause();
- mMediaControlView.show();
- } else {
- mMediaController.getTransportControls().play();
- mMediaControlView.hide();
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
- if (!mMediaPlayer.isPlaying()) {
- mMediaController.getTransportControls().play();
- mMediaControlView.hide();
- }
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP
- || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
- if (mMediaPlayer.isPlaying()) {
- mMediaController.getTransportControls().pause();
- mMediaControlView.show();
- }
- return true;
- } else {
- toggleMediaControlViewVisibility();
- }
- }
-
- return mSuperProvider.onKeyDown_impl(keyCode, event);
- }
-
- @Override
public void onFinishInflate_impl() {
mSuperProvider.onFinishInflate_impl();
}
@Override
- public boolean dispatchKeyEvent_impl(KeyEvent event) {
- return mSuperProvider.dispatchKeyEvent_impl(event);
- }
-
- @Override
public void setEnabled_impl(boolean enabled) {
mSuperProvider.setEnabled_impl(enabled);
}
@@ -494,7 +447,7 @@
}
mCurrentView = view;
if (mOnViewTypeChangedListener != null) {
- mOnViewTypeChangedListener.onViewTypeChanged(view.getViewType());
+ mOnViewTypeChangedListener.onViewTypeChanged(mInstance, view.getViewType());
}
if (needToStart()) {
mMediaController.getTransportControls().play();
@@ -556,12 +509,6 @@
controller.registerRenderer(new Cea708CaptionRenderer(context));
controller.registerRenderer(new ClosedCaptionRenderer(context));
mMediaPlayer.setSubtitleAnchor(controller, (SubtitleController.Anchor) mSubtitleView);
-
- if (mAudioSession != 0) {
- mMediaPlayer.setAudioSessionId(mAudioSession);
- } else {
- mAudioSession = mMediaPlayer.getAudioSessionId();
- }
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
mMediaPlayer.setOnCompletionListener(mCompletionListener);
@@ -660,8 +607,12 @@
}
mStateBuilder = new PlaybackState.Builder();
mStateBuilder.setActions(playbackActions);
- mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_SHOW_SUBTITLE, null, -1);
- mStateBuilder.addCustomAction(MediaControlView2Impl.COMMAND_HIDE_SUBTITLE, null, -1);
+
+ if (mCustomActionList != null) {
+ for (PlaybackState.CustomAction action : mCustomActionList) {
+ mStateBuilder.addCustomAction(action);
+ }
+ }
}
mStateBuilder.setState(getCorrespondingPlaybackState(),
mMediaPlayer.getCurrentPosition(), mSpeed);
@@ -754,7 +705,7 @@
}
mCurrentState = STATE_PREPARED;
if (mOnPreparedListener != null) {
- mOnPreparedListener.onPrepared();
+ mOnPreparedListener.onPrepared(mInstance);
}
if (mMediaControlView != null) {
mMediaControlView.setEnabled(true);
@@ -829,7 +780,7 @@
updatePlaybackState();
if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion();
+ mOnCompletionListener.onCompletion(mInstance);
}
if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) {
mAudioManager.abandonAudioFocus(null);
@@ -841,7 +792,7 @@
new MediaPlayer.OnInfoListener() {
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (mOnInfoListener != null) {
- mOnInfoListener.onInfo(what, extra);
+ mOnInfoListener.onInfo(mInstance, what, extra);
}
return true;
}
@@ -863,7 +814,7 @@
/* If an error handler has been supplied, use it and finish. */
if (mOnErrorListener != null) {
- if (mOnErrorListener.onError(frameworkErr, implErr)) {
+ if (mOnErrorListener.onError(mInstance, frameworkErr, implErr)) {
return true;
}
}
@@ -894,7 +845,7 @@
* at least inform them that the video is over.
*/
if (mOnCompletionListener != null) {
- mOnCompletionListener.onCompletion();
+ mOnCompletionListener.onCompletion(mInstance);
}
}
})
@@ -931,6 +882,11 @@
}
@Override
+ public void onCustomAction(String action, Bundle extras) {
+ mOnCustomActionListener.onCustomAction(action, extras);
+ }
+
+ @Override
public void onPlay() {
if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) {
applySpeed();
diff --git a/packages/MediaComponents/test/src/android/media/MediaController2Test.java b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
index ae67a95..3e39f40 100644
--- a/packages/MediaComponents/test/src/android/media/MediaController2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaController2Test.java
@@ -16,7 +16,7 @@
package android.media;
-import android.media.MediaPlayerBase.PlaybackListener;
+import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.SessionCallback;
import android.media.TestUtils.SyncHandler;
@@ -45,6 +45,7 @@
*/
// TODO(jaewan): Implement host-side test so controller and session can run in different processes.
// TODO(jaewan): Fix flaky failure -- see MediaController2Impl.getController()
+// TODO(jaeawn): Revisit create/close session in the sHandler. It's no longer necessary.
@RunWith(AndroidJUnit4.class)
@SmallTest
@FlakyTest
@@ -60,11 +61,10 @@
public void setUp() throws Exception {
super.setUp();
// Create this test specific MediaSession2 to use our own Handler.
- sHandler.postAndSync(()->{
- mPlayer = new MockPlayer(1);
- mSession = new MediaSession2.Builder(mContext, mPlayer).setId(TAG).build();
- });
-
+ mPlayer = new MockPlayer(1);
+ mSession = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback())
+ .setId(TAG).build();
mController = createController(mSession.getToken());
TestServiceRegistry.getInstance().setHandler(sHandler);
}
@@ -73,11 +73,9 @@
@Override
public void cleanUp() throws Exception {
super.cleanUp();
- sHandler.postAndSync(() -> {
- if (mSession != null) {
- mSession.close();
- }
- });
+ if (mSession != null) {
+ mSession.close();
+ }
TestServiceRegistry.getInstance().cleanUp();
}
@@ -103,7 +101,6 @@
assertTrue(mPlayer.mPauseCalled);
}
-
@Test
public void testSkipToPrevious() throws InterruptedException {
mController.skipToPrevious();
@@ -138,6 +135,65 @@
}
@Test
+ public void testPrepare() throws InterruptedException {
+ mController.prepare();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mPrepareCalled);
+ }
+
+ @Test
+ public void testFastForward() throws InterruptedException {
+ mController.fastForward();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mFastForwardCalled);
+ }
+
+ @Test
+ public void testRewind() throws InterruptedException {
+ mController.rewind();
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mRewindCalled);
+ }
+
+ @Test
+ public void testSeekTo() throws InterruptedException {
+ final long seekPosition = 12125L;
+ mController.seekTo(seekPosition);
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mSeekToCalled);
+ assertEquals(seekPosition, mPlayer.mSeekPosition);
+ }
+
+ @Test
+ public void testSetCurrentPlaylistItem() throws InterruptedException {
+ final int itemIndex = 9;
+ mController.setCurrentPlaylistItem(itemIndex);
+ try {
+ assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ assertTrue(mPlayer.mSetCurrentPlaylistItemCalled);
+ assertEquals(itemIndex, mPlayer.mItemIndex);
+ }
+
+ @Test
public void testGetPackageName() {
assertEquals(mContext.getPackageName(), mController.getSessionToken().getPackageName());
}
@@ -147,7 +203,7 @@
// TODO(jaewan): add equivalent test later
/*
final CountDownLatch latch = new CountDownLatch(1);
- final MediaPlayerBase.PlaybackListener listener = (state) -> {
+ final MediaPlayerInterface.PlaybackListener listener = (state) -> {
assertEquals(PlaybackState.STATE_BUFFERING, state.getState());
latch.countDown();
};
@@ -165,7 +221,7 @@
@Test
public void testAddPlaybackListener() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(2);
- final MediaPlayerBase.PlaybackListener listener = (state) -> {
+ final MediaPlayerInterface.PlaybackListener listener = (state) -> {
switch ((int) latch.getCount()) {
case 2:
assertEquals(PlaybackState.STATE_PLAYING, state.getState());
@@ -188,7 +244,7 @@
@Test
public void testRemovePlaybackListener() throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- final MediaPlayerBase.PlaybackListener listener = (state) -> {
+ final MediaPlayerInterface.PlaybackListener listener = (state) -> {
fail();
latch.countDown();
};
@@ -275,6 +331,7 @@
final MockPlayer player = new MockPlayer(0);
sessionHandler.postAndSync(() -> {
mSession = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback())
.setId("testDeadlock").build();
});
final MediaController2 controller = createController(mSession.getToken());
@@ -462,7 +519,9 @@
sHandler.postAndSync(() -> {
// Recreated session has different session stub, so previously created controller
// shouldn't be available.
- mSession = new MediaSession2.Builder(mContext, mPlayer).setId(id).build();
+ mSession = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback())
+ .setId(id).build();
});
testNoInteraction();
}
diff --git a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
index 045dcd5..8329cf0 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSession2Test.java
@@ -16,13 +16,10 @@
package android.media;
-import android.media.MediaPlayerBase.PlaybackListener;
import android.media.MediaSession2.Builder;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.SessionCallback;
-import android.media.session.PlaybackState;
import android.os.Process;
-import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -33,10 +30,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import static android.media.TestUtils.createPlaybackState;
import static org.junit.Assert.*;
/**
@@ -54,19 +49,16 @@
@Override
public void setUp() throws Exception {
super.setUp();
- sHandler.postAndSync(() -> {
- mPlayer = new MockPlayer(0);
- mSession = new MediaSession2.Builder(mContext, mPlayer).build();
- });
+ mPlayer = new MockPlayer(0);
+ mSession = new MediaSession2.Builder(mContext, mPlayer)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback()).build();
}
@After
@Override
public void cleanUp() throws Exception {
super.cleanUp();
- sHandler.postAndSync(() -> {
- mSession.close();
- });
+ mSession.close();
}
@Test
diff --git a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
index 192cbc2..6037619 100644
--- a/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
+++ b/packages/MediaComponents/test/src/android/media/MediaSessionManager_MediaSession2.java
@@ -59,9 +59,8 @@
// Specify TAG here so {@link MediaSession2.getInstance()} doesn't complaint about
// per test thread differs across the {@link MediaSession2} with the same TAG.
final MockPlayer player = new MockPlayer(1);
- sHandler.postAndSync(() -> {
- mSession = new MediaSession2.Builder(mContext, player).setId(TAG).build();
- });
+ mSession = new MediaSession2.Builder(mContext, player)
+ .setSessionCallback(sHandlerExecutor, new SessionCallback()).setId(TAG).build();
ensureChangeInSession();
}
@@ -70,9 +69,7 @@
public void cleanUp() throws Exception {
super.cleanUp();
sHandler.removeCallbacksAndMessages(null);
- sHandler.postAndSync(() -> {
- mSession.close();
- });
+ mSession.close();
}
// TODO(jaewan): Make this host-side test to see per-user behavior.
diff --git a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
index b058117..5c5c7d2 100644
--- a/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
+++ b/packages/MediaComponents/test/src/android/media/MockMediaSessionService2.java
@@ -28,6 +28,8 @@
import android.media.session.PlaybackState;
import android.os.Process;
+import java.util.concurrent.Executor;
+
/**
* Mock implementation of {@link android.media.MediaSessionService2} for testing.
*/
@@ -46,11 +48,12 @@
public MediaSession2 onCreateSession(String sessionId) {
final MockPlayer player = new MockPlayer(1);
final SyncHandler handler = (SyncHandler) TestServiceRegistry.getInstance().getHandler();
+ final Executor executor = (runnable) -> handler.post(runnable);
try {
handler.postAndSync(() -> {
mSession = new MediaSession2.Builder(MockMediaSessionService2.this, player)
- .setId(sessionId).setSessionCallback((runnable)->handler.post(runnable),
- new MySessionCallback()).build();
+ .setSessionCallback(executor, new MySessionCallback())
+ .setId(sessionId).build();
});
} catch (InterruptedException e) {
fail(e.toString());
diff --git a/packages/MediaComponents/test/src/android/media/MockPlayer.java b/packages/MediaComponents/test/src/android/media/MockPlayer.java
index ad7ba2f..d0d1178 100644
--- a/packages/MediaComponents/test/src/android/media/MockPlayer.java
+++ b/packages/MediaComponents/test/src/android/media/MockPlayer.java
@@ -16,6 +16,7 @@
package android.media;
+import android.media.MediaPlayerInterface;
import android.media.MediaSession2.PlaylistParams;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -26,9 +27,9 @@
import java.util.concurrent.Executor;
/**
- * A mock implementation of {@link MediaPlayerBase} for testing.
+ * A mock implementation of {@link MediaPlayerInterface} for testing.
*/
-public class MockPlayer extends MediaPlayerBase {
+public class MockPlayer implements MediaPlayerInterface {
public final CountDownLatch mCountDownLatch;
public boolean mPlayCalled;
@@ -36,6 +37,14 @@
public boolean mStopCalled;
public boolean mSkipToPreviousCalled;
public boolean mSkipToNextCalled;
+ public boolean mPrepareCalled;
+ public boolean mFastForwardCalled;
+ public boolean mRewindCalled;
+ public boolean mSeekToCalled;
+ public long mSeekPosition;
+ public boolean mSetCurrentPlaylistItemCalled;
+ public int mItemIndex;
+
public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
private PlaybackState2 mLastPlaybackState;
@@ -83,7 +92,47 @@
}
}
+ @Override
+ public void prepare() {
+ mPrepareCalled = true;
+ if (mCountDownLatch != null) {
+ mCountDownLatch.countDown();
+ }
+ }
+ @Override
+ public void fastForward() {
+ mFastForwardCalled = true;
+ if (mCountDownLatch != null) {
+ mCountDownLatch.countDown();
+ }
+ }
+
+ @Override
+ public void rewind() {
+ mRewindCalled = true;
+ if (mCountDownLatch != null) {
+ mCountDownLatch.countDown();
+ }
+ }
+
+ @Override
+ public void seekTo(long pos) {
+ mSeekToCalled = true;
+ mSeekPosition = pos;
+ if (mCountDownLatch != null) {
+ mCountDownLatch.countDown();
+ }
+ }
+
+ @Override
+ public void setCurrentPlaylistItem(int index) {
+ mSetCurrentPlaylistItemCalled = true;
+ mItemIndex = index;
+ if (mCountDownLatch != null) {
+ mCountDownLatch.countDown();
+ }
+ }
@Nullable
@Override
@@ -112,23 +161,6 @@
}
}
- // No-op. Should be added for test later.
- @Override
- public void prepare() {
- }
-
- @Override
- public void seekTo(long pos) {
- }
-
- @Override
- public void fastFoward() {
- }
-
- @Override
- public void rewind() {
- }
-
@Override
public AudioAttributes getAudioAttributes() {
return null;
@@ -137,8 +169,4 @@
@Override
public void setPlaylist(List<MediaItem2> item, PlaylistParams param) {
}
-
- @Override
- public void setCurrentPlaylistItem(int index) {
- }
}
diff --git a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
index 4e19d4d..0f1644c 100644
--- a/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
+++ b/packages/MediaComponents/test/src/android/media/PlaybackListenerHolder.java
@@ -16,10 +16,8 @@
package android.media;
-import android.media.MediaPlayerBase.PlaybackListener;
-import android.media.session.PlaybackState;
+import android.media.MediaPlayerInterface.PlaybackListener;
import android.os.Handler;
-import android.os.Message;
import android.support.annotation.NonNull;
import java.util.List;