Merge changes Iabb33606,I3c364ebe
* changes: Falling back to media session controls. Move activity to fullscreen stack when it is relaunched in PIP.
This commit is contained in:
@@ -31,8 +31,10 @@ oneway interface ITaskStackListener {
|
||||
* Called whenever IActivityManager.startActivity is called on an activity that is already
|
||||
* running in the pinned stack and the activity is not actually started, but the task is either
|
||||
* brought to the front or a new Intent is delivered to it.
|
||||
*
|
||||
* @param sourceComponent the component name of the activity that initiated the restart attempt
|
||||
*/
|
||||
void onPinnedActivityRestartAttempt();
|
||||
void onPinnedActivityRestartAttempt(in ComponentName sourceComponent);
|
||||
|
||||
/**
|
||||
* Called whenever the pinned stack is done animating a resize.
|
||||
|
||||
@@ -35,7 +35,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPinnedActivityRestartAttempt() throws RemoteException {
|
||||
public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,14 +16,20 @@
|
||||
|
||||
package com.android.systemui.pip.phone;
|
||||
|
||||
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.IActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.IPinnedStackController;
|
||||
import android.view.IPinnedStackListener;
|
||||
@@ -49,6 +55,7 @@ public class PipManager {
|
||||
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
|
||||
|
||||
private PipMenuActivityController mMenuController;
|
||||
private PipMediaController mMediaController;
|
||||
private PipTouchHandler mTouchHandler;
|
||||
|
||||
/**
|
||||
@@ -57,7 +64,11 @@ public class PipManager {
|
||||
TaskStackListener mTaskStackListener = new TaskStackListener() {
|
||||
@Override
|
||||
public void onActivityPinned() {
|
||||
if (!checkCurrentUserId(false /* debug */)) {
|
||||
return;
|
||||
}
|
||||
mTouchHandler.onActivityPinned();
|
||||
mMediaController.onActivityPinned();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,8 +77,24 @@ public class PipManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPinnedActivityRestartAttempt() {
|
||||
// TODO(winsonc): Hide the menu and expand the PiP
|
||||
public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) {
|
||||
if (!checkCurrentUserId(false /* debug */)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Expand the activity back to fullscreen only if it was attempted to be restarted from
|
||||
// another package than the top activity in the stack
|
||||
boolean expandPipToFullscreen = true;
|
||||
if (sourceComponent != null) {
|
||||
ComponentName topActivity = PipUtils.getTopPinnedActivity(mActivityManager);
|
||||
expandPipToFullscreen = topActivity != null && topActivity.getPackageName().equals(
|
||||
sourceComponent.getPackageName());
|
||||
}
|
||||
if (expandPipToFullscreen) {
|
||||
mTouchHandler.expandPinnedStackToFullscreen();
|
||||
} else {
|
||||
Log.w(TAG, "Can not expand PiP to fullscreen via intent from the same package.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +118,7 @@ public class PipManager {
|
||||
@Override
|
||||
public void onActionsChanged(ParceledListSlice actions) {
|
||||
mHandler.post(() -> {
|
||||
mMenuController.setActions(actions);
|
||||
mMenuController.setAppActions(actions);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,7 +154,9 @@ public class PipManager {
|
||||
}
|
||||
SystemServicesProxy.getInstance(mContext).registerTaskStackListener(mTaskStackListener);
|
||||
|
||||
mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager);
|
||||
mMediaController = new PipMediaController(context, mActivityManager);
|
||||
mMenuController = new PipMenuActivityController(context, mActivityManager, mWindowManager,
|
||||
mMediaController);
|
||||
mTouchHandler = new PipTouchHandler(context, mMenuController, mActivityManager,
|
||||
mWindowManager);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.systemui.pip.phone;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.app.RemoteAction;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaController.TransportControls;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.session.PlaybackState;
|
||||
|
||||
import com.android.systemui.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 {
|
||||
|
||||
/**
|
||||
* A listener interface to receive notification on changes to the media actions.
|
||||
*/
|
||||
public interface ActionListener {
|
||||
/**
|
||||
* Called when the media actions changes.
|
||||
*/
|
||||
void onMediaActionsChanged(List<RemoteAction> actions);
|
||||
}
|
||||
|
||||
private final Context mContext;
|
||||
private final IActivityManager mActivityManager;
|
||||
|
||||
private final MediaSessionManager mMediaSessionManager;
|
||||
private MediaController mMediaController;
|
||||
|
||||
private RemoteAction mPauseAction;
|
||||
private RemoteAction mPlayAction;
|
||||
|
||||
private MediaController.Callback mPlaybackChangedListener = new MediaController.Callback() {
|
||||
@Override
|
||||
public void onPlaybackStateChanged(PlaybackState state) {
|
||||
if (!mListeners.isEmpty()) {
|
||||
notifyActionsChanged(getMediaActions());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private ArrayList<ActionListener> mListeners = new ArrayList<>();
|
||||
|
||||
public PipMediaController(Context context, IActivityManager activityManager) {
|
||||
mContext = context;
|
||||
mActivityManager = activityManager;
|
||||
|
||||
createMediaActions();
|
||||
mMediaSessionManager =
|
||||
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
|
||||
mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> {
|
||||
resolveActiveMediaController(controllers);
|
||||
}, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.getActiveSessions(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new media action listener.
|
||||
*/
|
||||
public void addListener(ActionListener listener) {
|
||||
if (!mListeners.contains(listener)) {
|
||||
mListeners.add(listener);
|
||||
listener.onMediaActionsChanged(getMediaActions());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a media action listener.
|
||||
*/
|
||||
public void removeListener(ActionListener listener) {
|
||||
listener.onMediaActionsChanged(Collections.EMPTY_LIST);
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of media actions currently available.
|
||||
*/
|
||||
private List<RemoteAction> getMediaActions() {
|
||||
if (mMediaController == null || mMediaController.getPlaybackState() == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
ArrayList<RemoteAction> mediaActions = new ArrayList<>();
|
||||
int state = mMediaController.getPlaybackState().getState();
|
||||
boolean isPlaying = MediaSession.isActiveState(state);
|
||||
long actions = mMediaController.getPlaybackState().getActions();
|
||||
if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
|
||||
mediaActions.add(mPauseAction);
|
||||
} else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
|
||||
mediaActions.add(mPlayAction);
|
||||
}
|
||||
return mediaActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the standard media buttons that we may show.
|
||||
*/
|
||||
private void createMediaActions() {
|
||||
String pauseDescription = mContext.getString(R.string.pip_pause);
|
||||
mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
|
||||
R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription,
|
||||
action -> mMediaController.getTransportControls().pause());
|
||||
|
||||
String playDescription = mContext.getString(R.string.pip_play);
|
||||
mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
|
||||
R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription,
|
||||
action -> mMediaController.getTransportControls().play());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.getTopPinnedActivity(mActivityManager);
|
||||
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);
|
||||
}
|
||||
if (!mListeners.isEmpty()) {
|
||||
notifyActionsChanged(getMediaActions());
|
||||
}
|
||||
|
||||
// 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(List<RemoteAction> actions) {
|
||||
if (!mListeners.isEmpty()) {
|
||||
mListeners.forEach(l -> l.onMediaActionsChanged(actions));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.systemui.pip.phone;
|
||||
|
||||
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
|
||||
@@ -5,6 +21,7 @@ import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.RemoteAction;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
@@ -16,11 +33,20 @@ import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.IWindowManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import com.android.systemui.pip.phone.PipMediaController.ActionListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the PiP menu activity.
|
||||
*
|
||||
* The current media session provides actions whenever there are no valid actions provided by the
|
||||
* current PiP activity. Otherwise, those actions always take precedence.
|
||||
*/
|
||||
public class PipMenuActivityController {
|
||||
|
||||
private static final String TAG = "PipMenuActivityController";
|
||||
private static final String TAG = "PipMenuActController";
|
||||
|
||||
public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
|
||||
public static final String EXTRA_ACTIONS = "actions";
|
||||
@@ -59,9 +85,12 @@ public class PipMenuActivityController {
|
||||
private Context mContext;
|
||||
private IActivityManager mActivityManager;
|
||||
private IWindowManager mWindowManager;
|
||||
private PipMediaController mMediaController;
|
||||
|
||||
private ArrayList<Listener> mListeners = new ArrayList<>();
|
||||
private ParceledListSlice mActions;
|
||||
private ParceledListSlice mAppActions;
|
||||
private ParceledListSlice mMediaActions;
|
||||
private boolean mVisible;
|
||||
|
||||
private Messenger mToActivityMessenger;
|
||||
private Messenger mMessenger = new Messenger(new Handler() {
|
||||
@@ -70,13 +99,13 @@ public class PipMenuActivityController {
|
||||
switch (msg.what) {
|
||||
case MESSAGE_MENU_VISIBILITY_CHANGED: {
|
||||
boolean visible = msg.arg1 > 0;
|
||||
mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
|
||||
onMenuVisibilityChanged(visible);
|
||||
break;
|
||||
}
|
||||
case MESSAGE_EXPAND_PIP: {
|
||||
mListeners.forEach(l -> l.onPipExpand());
|
||||
// Preemptively mark the menu as invisible once we expand the PiP
|
||||
mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false));
|
||||
onMenuVisibilityChanged(false);
|
||||
break;
|
||||
}
|
||||
case MESSAGE_MINIMIZE_PIP: {
|
||||
@@ -86,14 +115,14 @@ public class PipMenuActivityController {
|
||||
case MESSAGE_DISMISS_PIP: {
|
||||
mListeners.forEach(l -> l.onPipDismiss());
|
||||
// Preemptively mark the menu as invisible once we dismiss the PiP
|
||||
mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false));
|
||||
onMenuVisibilityChanged(false);
|
||||
break;
|
||||
}
|
||||
case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
|
||||
mToActivityMessenger = msg.replyTo;
|
||||
// Mark the menu as invisible once the activity finishes as well
|
||||
if (mToActivityMessenger == null) {
|
||||
mListeners.forEach(l -> l.onPipMenuVisibilityChanged(false));
|
||||
onMenuVisibilityChanged(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -101,11 +130,20 @@ public class PipMenuActivityController {
|
||||
}
|
||||
});
|
||||
|
||||
private ActionListener mMediaActionListener = new ActionListener() {
|
||||
@Override
|
||||
public void onMediaActionsChanged(List<RemoteAction> mediaActions) {
|
||||
mMediaActions = new ParceledListSlice<>(mediaActions);
|
||||
updateMenuActions();
|
||||
}
|
||||
};
|
||||
|
||||
public PipMenuActivityController(Context context, IActivityManager activityManager,
|
||||
IWindowManager windowManager) {
|
||||
IWindowManager windowManager, PipMediaController mediaController) {
|
||||
mContext = context;
|
||||
mActivityManager = activityManager;
|
||||
mWindowManager = windowManager;
|
||||
mMediaController = mediaController;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +175,7 @@ public class PipMenuActivityController {
|
||||
pinnedStackInfo.taskIds.length > 0) {
|
||||
Intent intent = new Intent(mContext, PipMenuActivity.class);
|
||||
intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
|
||||
intent.putExtra(EXTRA_ACTIONS, mActions);
|
||||
intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
|
||||
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
|
||||
options.setLaunchTaskId(
|
||||
pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
|
||||
@@ -168,15 +206,31 @@ public class PipMenuActivityController {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@param actions} associated with the PiP.
|
||||
* Sets the menu actions to the actions provided by the current PiP activity.
|
||||
*/
|
||||
public void setActions(ParceledListSlice actions) {
|
||||
mActions = actions;
|
||||
public void setAppActions(ParceledListSlice appActions) {
|
||||
mAppActions = appActions;
|
||||
updateMenuActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the best set of actions to show in the PiP menu.
|
||||
*/
|
||||
private ParceledListSlice resolveMenuActions() {
|
||||
if (isValidActions(mAppActions)) {
|
||||
return mAppActions;
|
||||
}
|
||||
return mMediaActions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the PiP menu activity with the best set of actions provided.
|
||||
*/
|
||||
private void updateMenuActions() {
|
||||
if (mToActivityMessenger != null) {
|
||||
Message m = Message.obtain();
|
||||
m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS;
|
||||
m.obj = actions;
|
||||
m.obj = resolveMenuActions();
|
||||
try {
|
||||
mToActivityMessenger.send(m);
|
||||
} catch (RemoteException e) {
|
||||
@@ -184,4 +238,30 @@ public class PipMenuActivityController {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the set of actions are valid.
|
||||
*/
|
||||
private boolean isValidActions(ParceledListSlice actions) {
|
||||
return actions != null && actions.getList().size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changes in menu visibility.
|
||||
*/
|
||||
private void onMenuVisibilityChanged(boolean visible) {
|
||||
mListeners.forEach(l -> l.onPipMenuVisibilityChanged(visible));
|
||||
if (visible != mVisible) {
|
||||
if (visible) {
|
||||
// Once visible, start listening for media action changes. This call will trigger
|
||||
// the menu actions to be updated again.
|
||||
mMediaController.addListener(mMediaActionListener);
|
||||
} else {
|
||||
// Once hidden, stop listening for media action changes. This call will trigger
|
||||
// the menu actions to be updated again.
|
||||
mMediaController.removeListener(mMediaActionListener);
|
||||
}
|
||||
}
|
||||
mVisible = visible;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
/**
|
||||
* Resizes the pinned stack back to fullscreen.
|
||||
*/
|
||||
private void expandPinnedStackToFullscreen() {
|
||||
void expandPinnedStackToFullscreen() {
|
||||
BackgroundThread.getHandler().post(() -> {
|
||||
try {
|
||||
mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.systemui.pip.phone;
|
||||
|
||||
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
|
||||
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
import android.app.IActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
public class PipUtils {
|
||||
|
||||
private static final String TAG = "PipUtils";
|
||||
|
||||
/**
|
||||
* @return the ComponentName of the top activity in the pinned stack, or null if none exists.
|
||||
*/
|
||||
public static ComponentName getTopPinnedActivity(IActivityManager activityManager) {
|
||||
try {
|
||||
StackInfo pinnedStackInfo = activityManager.getStackInfo(PINNED_STACK_ID);
|
||||
if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
|
||||
pinnedStackInfo.taskIds.length > 0) {
|
||||
return pinnedStackInfo.topActivity;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Unable to get pinned stack.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -590,7 +590,7 @@ public class PipManager {
|
||||
@Override
|
||||
public void onTaskStackChanged() {
|
||||
if (DEBUG) Log.d(TAG, "onTaskStackChanged()");
|
||||
if (!checkCurrentUserId()) {
|
||||
if (!checkCurrentUserId(DEBUG)) {
|
||||
return;
|
||||
}
|
||||
if (mState != STATE_NO_PIP) {
|
||||
@@ -627,7 +627,7 @@ public class PipManager {
|
||||
@Override
|
||||
public void onActivityPinned() {
|
||||
if (DEBUG) Log.d(TAG, "onActivityPinned()");
|
||||
if (!checkCurrentUserId()) {
|
||||
if (!checkCurrentUserId(DEBUG)) {
|
||||
return;
|
||||
}
|
||||
StackInfo stackInfo = getPinnedStackInfo();
|
||||
@@ -658,9 +658,9 @@ public class PipManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPinnedActivityRestartAttempt() {
|
||||
public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) {
|
||||
if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
|
||||
if (!checkCurrentUserId()) {
|
||||
if (!checkCurrentUserId(DEBUG)) {
|
||||
return;
|
||||
}
|
||||
// If PIPed activity is launched again by Launcher or intent, make it fullscreen.
|
||||
@@ -670,7 +670,7 @@ public class PipManager {
|
||||
@Override
|
||||
public void onPinnedStackAnimationEnded() {
|
||||
if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
|
||||
if (!checkCurrentUserId()) {
|
||||
if (!checkCurrentUserId(DEBUG)) {
|
||||
return;
|
||||
}
|
||||
switch (mState) {
|
||||
@@ -693,26 +693,6 @@ public class PipManager {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// {@link android.app.ITaskStackListener} isn't multi-user aware.
|
||||
// Check the current uid and current SystemUI's running uid
|
||||
// so we can handle the PIP status change only once.
|
||||
private boolean checkCurrentUserId() {
|
||||
try {
|
||||
int processUserId = UserHandle.myUserId();
|
||||
int currentUserId = mActivityManager.getCurrentUser().id;
|
||||
if (processUserId != currentUserId) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
|
||||
+ " and the current user is uid=" + currentUserId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Unable to get current user.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -79,6 +79,7 @@ import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.internal.app.AssistUtils;
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
import com.android.keyguard.KeyguardUpdateMonitor;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.pip.tv.PipMenuActivity;
|
||||
import com.android.systemui.pip.tv.PipOnboardingActivity;
|
||||
@@ -152,11 +153,30 @@ public class SystemServicesProxy {
|
||||
public void onTaskStackChanged() { }
|
||||
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
|
||||
public void onActivityPinned() { }
|
||||
public void onPinnedActivityRestartAttempt() { }
|
||||
public void onPinnedActivityRestartAttempt(ComponentName sourceComponent) { }
|
||||
public void onPinnedStackAnimationEnded() { }
|
||||
public void onActivityForcedResizable(String packageName, int taskId) { }
|
||||
public void onActivityDismissingDockedStack() { }
|
||||
public void onTaskProfileLocked(int taskId, int userId) { }
|
||||
|
||||
/**
|
||||
* Checks that the current user matches the user's SystemUI process. Since
|
||||
* {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
|
||||
* TaskStackListener should make this call to verify that we don't act on events from other
|
||||
* user's processes.
|
||||
*/
|
||||
protected final boolean checkCurrentUserId(boolean debug) {
|
||||
int processUserId = UserHandle.myUserId();
|
||||
int currentUserId = KeyguardUpdateMonitor.getCurrentUser();
|
||||
if (processUserId != currentUserId) {
|
||||
if (debug) {
|
||||
Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
|
||||
+ " and the current user is uid=" + currentUserId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,9 +198,11 @@ public class SystemServicesProxy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPinnedActivityRestartAttempt() throws RemoteException{
|
||||
public void onPinnedActivityRestartAttempt(ComponentName sourceComponent)
|
||||
throws RemoteException{
|
||||
mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
|
||||
mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
|
||||
mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, sourceComponent)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1214,7 +1236,8 @@ public class SystemServicesProxy {
|
||||
}
|
||||
case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
|
||||
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
|
||||
mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
|
||||
mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
|
||||
(ComponentName) msg.obj);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -582,7 +582,10 @@ class ActivityStarter {
|
||||
// The activity was already running in the pinned stack so it wasn't started, but either
|
||||
// brought to the front or the new intent was delivered to it since it was already in
|
||||
// front. Notify anyone interested in this piece of information.
|
||||
mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt();
|
||||
final ComponentName sourceComponent = sourceRecord == null ? null :
|
||||
sourceRecord.realActivity;
|
||||
mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
|
||||
sourceComponent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class TaskChangeNotificationController {
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyPinnedActivityRestartAttempt = (l, m) -> {
|
||||
l.onPinnedActivityRestartAttempt();
|
||||
l.onPinnedActivityRestartAttempt((ComponentName) m.obj);
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyPinnedStackAnimationEnded = (l, m) -> {
|
||||
@@ -267,10 +267,11 @@ class TaskChangeNotificationController {
|
||||
* running in the pinned stack and the activity was not actually started, but the task is
|
||||
* either brought to the front or a new Intent is delivered to it.
|
||||
*/
|
||||
void notifyPinnedActivityRestartAttempt() {
|
||||
void notifyPinnedActivityRestartAttempt(ComponentName sourceComponent) {
|
||||
mHandler.removeMessages(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
|
||||
final Message msg =
|
||||
mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG);
|
||||
mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG,
|
||||
sourceComponent);
|
||||
forAllLocalListeners(mNotifyPinnedActivityRestartAttempt, msg);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user