blob: 911245043e0b7c64bb446855ad5f28c517307d78 [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 android.media;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.media.update.ApiLoader;
import android.media.update.MediaController2Provider;
import android.os.Handler;
import java.util.concurrent.Executor;
/**
* Allows an app to interact with an active {@link MediaSession2} or a
* {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
* the session.
* <p>
* When you're done, use {@link #release()} to clean up resources. This also helps session service
* to be destroyed when there's no controller associated with it.
* <p>
* When controlling {@link MediaSession2}, the controller will be available immediately after
* the creation.
* <p>
* When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
* available only if the session service allows this controller by
* {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait
* {@link ControllerCallback#onConnected(CommandGroup)} or
* {@link ControllerCallback#onDisconnected()} for the result.
* <p>
* A controller can be created through token from {@link MediaSessionManager} if you hold the
* signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
* an enabled notification listener or by getting a {@link SessionToken} directly the
* the session owner.
* <p>
* MediaController2 objects are thread-safe.
* <p>
* @see MediaSession2
* @see MediaSessionService2
* @hide
*/
// TODO(jaewan): Unhide
// TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing.
public class MediaController2 extends MediaPlayerBase {
/**
* Interface for listening to change in activeness of the {@link MediaSession2}. It's
* active if and only if it has set a player.
*/
public abstract static class ControllerCallback {
/**
* Called when the controller is successfully connected to the session. The controller
* becomes available afterwards.
*
* @param allowedCommands commands that's allowed by the session.
*/
public void onConnected(CommandGroup allowedCommands) { }
/**
* Called when the session refuses the controller or the controller is disconnected from
* the session. The controller becomes unavailable afterwards and the callback wouldn't
* be called.
* <p>
* It will be also called after the {@link #release()}, so you can put clean up code here.
* You don't need to call {@link #release()} after this.
*/
public void onDisconnected() { }
}
private final MediaController2Provider mProvider;
/**
* Create a {@link MediaController2} from the {@link SessionToken}. This connects to the session
* and may wake up the service if it's not available.
*
* @param context Context
* @param token token to connect to
* @param callback controller callback to receive changes in
* @param executor executor to run callbacks on.
*/
// TODO(jaewan): Put @CallbackExecutor to the constructor.
public MediaController2(@NonNull Context context, @NonNull SessionToken token,
@NonNull ControllerCallback callback, @NonNull Executor executor) {
super();
// This also connects to the token.
// Explicit connect() isn't added on purpose because retrying connect() is impossible with
// session whose session binder is only valid while it's active.
// prevent a controller from reusable after the
// session is released and recreated.
mProvider = createProvider(context, token, callback, executor);
}
MediaController2Provider createProvider(@NonNull Context context,
@NonNull SessionToken token, @NonNull ControllerCallback callback,
@NonNull Executor executor) {
return ApiLoader.getProvider(context)
.createMediaController2(this, context, token, callback, executor);
}
/**
* Release this object, and disconnect from the session. After this, callbacks wouldn't be
* received.
*/
public void release() {
mProvider.release_impl();
}
/**
* @hide
*/
public MediaController2Provider getProvider() {
return mProvider;
}
/**
* @return token
*/
public @NonNull
SessionToken getSessionToken() {
return mProvider.getSessionToken_impl();
}
/**
* Returns whether this class is connected to active {@link MediaSession2} or not.
*/
public boolean isConnected() {
return mProvider.isConnected_impl();
}
@Override
public void play() {
mProvider.play_impl();
}
@Override
public void pause() {
mProvider.pause_impl();
}
@Override
public void stop() {
mProvider.stop_impl();
}
@Override
public void skipToPrevious() {
mProvider.skipToPrevious_impl();
}
@Override
public void skipToNext() {
mProvider.skipToNext_impl();
}
@Override
public @Nullable PlaybackState getPlaybackState() {
return mProvider.getPlaybackState_impl();
}
/**
* Add a {@link PlaybackListener} to listen changes in the
* {@link MediaSession2}.
*
* @param listener the listener that will be run
* @param handler the Handler that will receive the listener
* @throws IllegalArgumentException Called when either the listener or handler is {@code null}.
*/
// TODO(jaewan): Match with the addSessionAvailabilityListener() that tells the current state
// through the listener.
// TODO(jaewan): Can handler be null? Follow the API guideline after it's finalized.
@Override
public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
mProvider.addPlaybackListener_impl(listener, handler);
}
/**
* Remove previously added {@link PlaybackListener}.
*
* @param listener the listener to be removed
* @throws IllegalArgumentException if the listener is {@code null}.
*/
@Override
public void removePlaybackListener(@NonNull PlaybackListener listener) {
mProvider.removePlaybackListener_impl(listener);
}
}