Moving shared classes to common.pip package 3/N

- PipMediaController is moved to common package and converted to Kotlin

Bug: 293306185
Test: makepush sysuig
Test: m -j ArcSystemUI
Change-Id: I86a7f1570e6db4588972384f13e0cfabd321c3fe
This commit is contained in:
Hongwei Wang
2023-08-18 15:31:31 -07:00
parent 183c5b024b
commit 6f31b5bb94
13 changed files with 383 additions and 390 deletions

View File

@@ -0,0 +1,365 @@
/*
* Copyright (C) 2023 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 com.android.wm.shell.common.pip
import android.annotation.DrawableRes
import android.annotation.StringRes
import android.app.PendingIntent
import android.app.RemoteAction
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.drawable.Icon
import android.media.MediaMetadata
import android.media.session.MediaController
import android.media.session.MediaSession
import android.media.session.MediaSessionManager
import android.media.session.PlaybackState
import android.os.Handler
import android.os.HandlerExecutor
import android.os.UserHandle
import com.android.wm.shell.R
import com.android.wm.shell.pip.PipUtils
import java.util.function.Consumer
/**
* Interfaces with the [MediaSessionManager] to compose the right set of actions to show (only
* if there are no actions from the PiP activity itself). The active media controller is only set
* when there is a media session from the top PiP activity.
*/
class PipMediaController(private val mContext: Context, private val mMainHandler: Handler) {
/**
* A listener interface to receive notification on changes to the media actions.
*/
interface ActionListener {
/**
* Called when the media actions changed.
*/
fun onMediaActionsChanged(actions: List<RemoteAction?>?)
}
/**
* A listener interface to receive notification on changes to the media metadata.
*/
interface MetadataListener {
/**
* Called when the media metadata changed.
*/
fun onMediaMetadataChanged(metadata: MediaMetadata?)
}
/**
* A listener interface to receive notification on changes to the media session token.
*/
interface TokenListener {
/**
* Called when the media session token changed.
*/
fun onMediaSessionTokenChanged(token: MediaSession.Token?)
}
private val mHandlerExecutor: HandlerExecutor = HandlerExecutor(mMainHandler)
private val mMediaSessionManager: MediaSessionManager?
private var mMediaController: MediaController? = null
private val mPauseAction: RemoteAction
private val mPlayAction: RemoteAction
private val mNextAction: RemoteAction
private val mPrevAction: RemoteAction
private val mMediaActionReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (mMediaController == null) {
// no active media session, bail early.
return
}
when (intent.action) {
ACTION_PLAY -> mMediaController!!.transportControls.play()
ACTION_PAUSE -> mMediaController!!.transportControls.pause()
ACTION_NEXT -> mMediaController!!.transportControls.skipToNext()
ACTION_PREV -> mMediaController!!.transportControls.skipToPrevious()
}
}
}
private val mPlaybackChangedListener: MediaController.Callback =
object : MediaController.Callback() {
override fun onPlaybackStateChanged(state: PlaybackState?) {
notifyActionsChanged()
}
override fun onMetadataChanged(metadata: MediaMetadata?) {
notifyMetadataChanged(metadata)
}
}
private val mSessionsChangedListener =
MediaSessionManager.OnActiveSessionsChangedListener { controllers: List<MediaController>? ->
resolveActiveMediaController(controllers)
}
private val mActionListeners = ArrayList<ActionListener>()
private val mMetadataListeners = ArrayList<MetadataListener>()
private val mTokenListeners = ArrayList<TokenListener>()
init {
val mediaControlFilter = IntentFilter()
mediaControlFilter.addAction(ACTION_PLAY)
mediaControlFilter.addAction(ACTION_PAUSE)
mediaControlFilter.addAction(ACTION_NEXT)
mediaControlFilter.addAction(ACTION_PREV)
mContext.registerReceiverForAllUsers(
mMediaActionReceiver, mediaControlFilter,
SYSTEMUI_PERMISSION, mMainHandler, Context.RECEIVER_EXPORTED
)
// Creates the standard media buttons that we may show.
mPauseAction = getDefaultRemoteAction(
R.string.pip_pause,
R.drawable.pip_ic_pause_white, ACTION_PAUSE
)
mPlayAction = getDefaultRemoteAction(
R.string.pip_play,
R.drawable.pip_ic_play_arrow_white, ACTION_PLAY
)
mNextAction = getDefaultRemoteAction(
R.string.pip_skip_to_next,
R.drawable.pip_ic_skip_next_white, ACTION_NEXT
)
mPrevAction = getDefaultRemoteAction(
R.string.pip_skip_to_prev,
R.drawable.pip_ic_skip_previous_white, ACTION_PREV
)
mMediaSessionManager = mContext.getSystemService(
MediaSessionManager::class.java
)
}
/**
* Handles when an activity is pinned.
*/
fun onActivityPinned() {
// Once we enter PiP, try to find the active media controller for the top most activity
resolveActiveMediaController(
mMediaSessionManager!!.getActiveSessionsForUser(
null,
UserHandle.CURRENT
)
)
}
/**
* Adds a new media action listener.
*/
fun addActionListener(listener: ActionListener) {
if (!mActionListeners.contains(listener)) {
mActionListeners.add(listener)
listener.onMediaActionsChanged(mediaActions)
}
}
/**
* Removes a media action listener.
*/
fun removeActionListener(listener: ActionListener) {
listener.onMediaActionsChanged(emptyList<RemoteAction>())
mActionListeners.remove(listener)
}
/**
* Adds a new media metadata listener.
*/
fun addMetadataListener(listener: MetadataListener) {
if (!mMetadataListeners.contains(listener)) {
mMetadataListeners.add(listener)
listener.onMediaMetadataChanged(mediaMetadata)
}
}
/**
* Removes a media metadata listener.
*/
fun removeMetadataListener(listener: MetadataListener) {
listener.onMediaMetadataChanged(null)
mMetadataListeners.remove(listener)
}
/**
* Adds a new token listener.
*/
fun addTokenListener(listener: TokenListener) {
if (!mTokenListeners.contains(listener)) {
mTokenListeners.add(listener)
listener.onMediaSessionTokenChanged(token)
}
}
/**
* Removes a token listener.
*/
fun removeTokenListener(listener: TokenListener) {
listener.onMediaSessionTokenChanged(null)
mTokenListeners.remove(listener)
}
private val token: MediaSession.Token?
get() = if (mMediaController == null) {
null
} else mMediaController!!.sessionToken
private val mediaMetadata: MediaMetadata?
get() = if (mMediaController != null) mMediaController!!.metadata else null
private val mediaActions: List<RemoteAction?>
/**
* Gets the set of media actions currently available.
*/
get() {
if (mMediaController == null) {
return emptyList<RemoteAction>()
}
// Cache the PlaybackState since it's a Binder call.
// Safe because mMediaController is guaranteed non-null here.
val playbackState: PlaybackState = mMediaController!!.playbackState
?: return emptyList<RemoteAction>()
val mediaActions = ArrayList<RemoteAction?>()
val isPlaying = playbackState.isActive
val actions = playbackState.actions
// Prev action
mPrevAction.isEnabled =
actions and PlaybackState.ACTION_SKIP_TO_PREVIOUS != 0L
mediaActions.add(mPrevAction)
// Play/pause action
if (!isPlaying && actions and PlaybackState.ACTION_PLAY != 0L) {
mediaActions.add(mPlayAction)
} else if (isPlaying && actions and PlaybackState.ACTION_PAUSE != 0L) {
mediaActions.add(mPauseAction)
}
// Next action
mNextAction.isEnabled =
actions and PlaybackState.ACTION_SKIP_TO_NEXT != 0L
mediaActions.add(mNextAction)
return mediaActions
}
/** @return Default [RemoteAction] sends broadcast back to SysUI.
*/
private fun getDefaultRemoteAction(
@StringRes titleAndDescription: Int,
@DrawableRes icon: Int,
action: String
): RemoteAction {
val titleAndDescriptionStr = mContext.getString(titleAndDescription)
val intent = Intent(action)
intent.setPackage(mContext.packageName)
return RemoteAction(
Icon.createWithResource(mContext, icon),
titleAndDescriptionStr, titleAndDescriptionStr,
PendingIntent.getBroadcast(
mContext, 0 /* requestCode */, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
}
/**
* Re-registers the session listener for the current user.
*/
fun registerSessionListenerForCurrentUser() {
mMediaSessionManager!!.removeOnActiveSessionsChangedListener(mSessionsChangedListener)
mMediaSessionManager.addOnActiveSessionsChangedListener(
null, UserHandle.CURRENT,
mHandlerExecutor, mSessionsChangedListener
)
}
/**
* Tries to find and set the active media controller for the top PiP activity.
*/
private fun resolveActiveMediaController(controllers: List<MediaController>?) {
if (controllers != null) {
val topActivity = PipUtils.getTopPipActivity(mContext).first
if (topActivity != null) {
for (i in controllers.indices) {
val controller = controllers[i]
if (controller.packageName == topActivity.packageName) {
setActiveMediaController(controller)
return
}
}
}
}
setActiveMediaController(null)
}
/**
* Sets the active media controller for the top PiP activity.
*/
private fun setActiveMediaController(controller: MediaController?) {
if (controller != mMediaController) {
if (mMediaController != null) {
mMediaController!!.unregisterCallback(mPlaybackChangedListener)
}
mMediaController = controller
controller?.registerCallback(mPlaybackChangedListener, mMainHandler)
notifyActionsChanged()
notifyMetadataChanged(mediaMetadata)
notifyTokenChanged(token)
// TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
}
}
/**
* Notifies all listeners that the actions have changed.
*/
private fun notifyActionsChanged() {
if (mActionListeners.isNotEmpty()) {
val actions = mediaActions
mActionListeners.forEach(
Consumer { l: ActionListener -> l.onMediaActionsChanged(actions) })
}
}
/**
* Notifies all listeners that the metadata have changed.
*/
private fun notifyMetadataChanged(metadata: MediaMetadata?) {
if (mMetadataListeners.isNotEmpty()) {
mMetadataListeners.forEach(Consumer { l: MetadataListener ->
l.onMediaMetadataChanged(
metadata
)
})
}
}
private fun notifyTokenChanged(token: MediaSession.Token?) {
if (mTokenListeners.isNotEmpty()) {
mTokenListeners.forEach(Consumer { l: TokenListener ->
l.onMediaSessionTokenChanged(
token
)
})
}
}
companion object {
private const val SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF"
private const val ACTION_PLAY = "com.android.wm.shell.pip.PLAY"
private const val ACTION_PAUSE = "com.android.wm.shell.pip.PAUSE"
private const val ACTION_NEXT = "com.android.wm.shell.pip.NEXT"
private const val ACTION_PREV = "com.android.wm.shell.pip.PREV"
}
}

View File

@@ -57,6 +57,7 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
@@ -332,6 +333,13 @@ public abstract class WMShellBaseModule {
return new PipUiEventLogger(uiEventLogger, packageManager);
}
@WMSingleton
@Provides
static PipMediaController providePipMediaController(Context context,
@ShellMainThread Handler mainHandler) {
return new PipMediaController(context, mainHandler);
}
//
// Bubbles (optional feature)

View File

@@ -32,6 +32,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
@@ -42,7 +43,6 @@ import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;

View File

@@ -17,11 +17,8 @@
package com.android.wm.shell.dagger.pip;
import android.content.Context;
import android.os.Handler;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import dagger.Module;
@@ -33,14 +30,6 @@ import dagger.Provides;
*/
@Module
public abstract class Pip1SharedModule {
// Needs handler for registering broadcast receivers
@WMSingleton
@Provides
static PipMediaController providePipMediaController(Context context,
@ShellMainThread Handler mainHandler) {
return new PipMediaController(context, mainHandler);
}
@WMSingleton
@Provides
static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {

View File

@@ -30,6 +30,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
@@ -37,7 +38,6 @@ import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;

View File

@@ -1,369 +0,0 @@
/*
* Copyright (C) 2020 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 com.android.wm.shell.pip;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import android.annotation.DrawableRes;
import android.annotation.StringRes;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Interfaces with the {@link MediaSessionManager} to compose the right set of actions to show (only
* if there are no actions from the PiP activity itself). The active media controller is only set
* when there is a media session from the top PiP activity.
*/
public class PipMediaController {
private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
private static final String ACTION_PLAY = "com.android.wm.shell.pip.PLAY";
private static final String ACTION_PAUSE = "com.android.wm.shell.pip.PAUSE";
private static final String ACTION_NEXT = "com.android.wm.shell.pip.NEXT";
private static final String ACTION_PREV = "com.android.wm.shell.pip.PREV";
/**
* A listener interface to receive notification on changes to the media actions.
*/
public interface ActionListener {
/**
* Called when the media actions changed.
*/
void onMediaActionsChanged(List<RemoteAction> actions);
}
/**
* A listener interface to receive notification on changes to the media metadata.
*/
public interface MetadataListener {
/**
* Called when the media metadata changed.
*/
void onMediaMetadataChanged(MediaMetadata metadata);
}
/**
* A listener interface to receive notification on changes to the media session token.
*/
public interface TokenListener {
/**
* Called when the media session token changed.
*/
void onMediaSessionTokenChanged(MediaSession.Token token);
}
private final Context mContext;
private final Handler mMainHandler;
private final HandlerExecutor mHandlerExecutor;
private final MediaSessionManager mMediaSessionManager;
private MediaController mMediaController;
private RemoteAction mPauseAction;
private RemoteAction mPlayAction;
private RemoteAction mNextAction;
private RemoteAction mPrevAction;
private final BroadcastReceiver mMediaActionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mMediaController == null || mMediaController.getTransportControls() == null) {
// no active media session, bail early.
return;
}
switch (intent.getAction()) {
case ACTION_PLAY:
mMediaController.getTransportControls().play();
break;
case ACTION_PAUSE:
mMediaController.getTransportControls().pause();
break;
case ACTION_NEXT:
mMediaController.getTransportControls().skipToNext();
break;
case ACTION_PREV:
mMediaController.getTransportControls().skipToPrevious();
break;
}
}
};
private final MediaController.Callback mPlaybackChangedListener =
new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
notifyActionsChanged();
}
@Override
public void onMetadataChanged(@Nullable MediaMetadata metadata) {
notifyMetadataChanged(metadata);
}
};
private final MediaSessionManager.OnActiveSessionsChangedListener mSessionsChangedListener =
this::resolveActiveMediaController;
private final ArrayList<ActionListener> mActionListeners = new ArrayList<>();
private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>();
private final ArrayList<TokenListener> mTokenListeners = new ArrayList<>();
public PipMediaController(Context context, Handler mainHandler) {
mContext = context;
mMainHandler = mainHandler;
mHandlerExecutor = new HandlerExecutor(mMainHandler);
if (!PipUtils.isPip2ExperimentEnabled()) {
IntentFilter mediaControlFilter = new IntentFilter();
mediaControlFilter.addAction(ACTION_PLAY);
mediaControlFilter.addAction(ACTION_PAUSE);
mediaControlFilter.addAction(ACTION_NEXT);
mediaControlFilter.addAction(ACTION_PREV);
mContext.registerReceiverForAllUsers(mMediaActionReceiver, mediaControlFilter,
SYSTEMUI_PERMISSION, mainHandler, Context.RECEIVER_EXPORTED);
}
// Creates the standard media buttons that we may show.
mPauseAction = getDefaultRemoteAction(R.string.pip_pause,
R.drawable.pip_ic_pause_white, ACTION_PAUSE);
mPlayAction = getDefaultRemoteAction(R.string.pip_play,
R.drawable.pip_ic_play_arrow_white, ACTION_PLAY);
mNextAction = getDefaultRemoteAction(R.string.pip_skip_to_next,
R.drawable.pip_ic_skip_next_white, ACTION_NEXT);
mPrevAction = getDefaultRemoteAction(R.string.pip_skip_to_prev,
R.drawable.pip_ic_skip_previous_white, ACTION_PREV);
mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
}
/**
* Handles when an activity is pinned.
*/
public void onActivityPinned() {
// Once we enter PiP, try to find the active media controller for the top most activity
resolveActiveMediaController(mMediaSessionManager.getActiveSessionsForUser(null,
UserHandle.CURRENT));
}
/**
* Adds a new media action listener.
*/
public void addActionListener(ActionListener listener) {
if (!mActionListeners.contains(listener)) {
mActionListeners.add(listener);
listener.onMediaActionsChanged(getMediaActions());
}
}
/**
* Removes a media action listener.
*/
public void removeActionListener(ActionListener listener) {
listener.onMediaActionsChanged(Collections.emptyList());
mActionListeners.remove(listener);
}
/**
* Adds a new media metadata listener.
*/
public void addMetadataListener(MetadataListener listener) {
if (!mMetadataListeners.contains(listener)) {
mMetadataListeners.add(listener);
listener.onMediaMetadataChanged(getMediaMetadata());
}
}
/**
* Removes a media metadata listener.
*/
public void removeMetadataListener(MetadataListener listener) {
listener.onMediaMetadataChanged(null);
mMetadataListeners.remove(listener);
}
/**
* Adds a new token listener.
*/
public void addTokenListener(TokenListener listener) {
if (!mTokenListeners.contains(listener)) {
mTokenListeners.add(listener);
listener.onMediaSessionTokenChanged(getToken());
}
}
/**
* Removes a token listener.
*/
public void removeTokenListener(TokenListener listener) {
listener.onMediaSessionTokenChanged(null);
mTokenListeners.remove(listener);
}
private MediaSession.Token getToken() {
if (mMediaController == null) {
return null;
}
return mMediaController.getSessionToken();
}
private MediaMetadata getMediaMetadata() {
return mMediaController != null ? mMediaController.getMetadata() : null;
}
/**
* Gets the set of media actions currently available.
*/
// This is due to using PlaybackState#isActive, which is added in API 31.
// It can be removed when min_sdk of the app is set to 31 or greater.
@SuppressLint("NewApi")
private List<RemoteAction> getMediaActions() {
// Cache the PlaybackState since it's a Binder call.
final PlaybackState playbackState;
if (mMediaController == null
|| (playbackState = mMediaController.getPlaybackState()) == null) {
return Collections.emptyList();
}
ArrayList<RemoteAction> mediaActions = new ArrayList<>();
boolean isPlaying = playbackState.isActive();
long actions = playbackState.getActions();
// Prev action
mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
mediaActions.add(mPrevAction);
// Play/pause action
if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
mediaActions.add(mPlayAction);
} else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
mediaActions.add(mPauseAction);
}
// Next action
mNextAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
mediaActions.add(mNextAction);
return mediaActions;
}
/** @return Default {@link RemoteAction} sends broadcast back to SysUI. */
private RemoteAction getDefaultRemoteAction(@StringRes int titleAndDescription,
@DrawableRes int icon, String action) {
final String titleAndDescriptionStr = mContext.getString(titleAndDescription);
final Intent intent = new Intent(action);
intent.setPackage(mContext.getPackageName());
return new RemoteAction(Icon.createWithResource(mContext, icon),
titleAndDescriptionStr, titleAndDescriptionStr,
PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
}
/**
* Re-registers the session listener for the current user.
*/
public void registerSessionListenerForCurrentUser() {
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener);
mMediaSessionManager.addOnActiveSessionsChangedListener(null, UserHandle.CURRENT,
mHandlerExecutor, mSessionsChangedListener);
}
/**
* Tries to find and set the active media controller for the top PiP activity.
*/
private void resolveActiveMediaController(List<MediaController> controllers) {
if (controllers != null) {
final ComponentName topActivity = PipUtils.getTopPipActivity(mContext).first;
if (topActivity != null) {
for (int i = 0; i < controllers.size(); i++) {
final MediaController controller = controllers.get(i);
if (controller.getPackageName().equals(topActivity.getPackageName())) {
setActiveMediaController(controller);
return;
}
}
}
}
setActiveMediaController(null);
}
/**
* Sets the active media controller for the top PiP activity.
*/
private void setActiveMediaController(MediaController controller) {
if (controller != mMediaController) {
if (mMediaController != null) {
mMediaController.unregisterCallback(mPlaybackChangedListener);
}
mMediaController = controller;
if (controller != null) {
controller.registerCallback(mPlaybackChangedListener, mMainHandler);
}
notifyActionsChanged();
notifyMetadataChanged(getMediaMetadata());
notifyTokenChanged(getToken());
// TODO(winsonc): Consider if we want to close the PIP after a timeout (like on TV)
}
}
/**
* Notifies all listeners that the actions have changed.
*/
private void notifyActionsChanged() {
if (!mActionListeners.isEmpty()) {
List<RemoteAction> actions = getMediaActions();
mActionListeners.forEach(l -> l.onMediaActionsChanged(actions));
}
}
/**
* Notifies all listeners that the metadata have changed.
*/
private void notifyMetadataChanged(MediaMetadata metadata) {
if (!mMetadataListeners.isEmpty()) {
mMetadataListeners.forEach(l -> l.onMediaMetadataChanged(metadata));
}
}
private void notifyTokenChanged(MediaSession.Token token) {
if (!mTokenListeners.isEmpty()) {
mTokenListeners.forEach(l -> l.onMediaSessionTokenChanged(token));
}
}
}

View File

@@ -38,10 +38,10 @@ import android.view.WindowManagerGlobal;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipMediaController.ActionListener;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

View File

@@ -76,6 +76,7 @@ import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
import com.android.wm.shell.pip.IPip;
@@ -87,7 +88,6 @@ import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;

View File

@@ -35,7 +35,7 @@ import android.content.Context;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

View File

@@ -48,11 +48,11 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;

View File

@@ -36,7 +36,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ImageUtils;
import com.android.wm.shell.R;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

View File

@@ -56,12 +56,12 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;

View File

@@ -37,7 +37,7 @@ import android.testing.TestableLooper;
import android.util.Log;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController;
import org.junit.Before;
import org.junit.Test;