Merge changes from topic "shell_feature_interfaces_reland" into sc-dev

* changes:
  2/ Add mechanism to expose other features directly
  1/ Add mechanism to expose shell feature directly (for Pip)
This commit is contained in:
Winson Chung
2021-03-19 06:55:31 +00:00
committed by Android (Google) Code Review
35 changed files with 1064 additions and 927 deletions

View File

@@ -38,6 +38,14 @@ filegroup {
path: "src",
}
filegroup {
name: "wm_shell-aidls",
srcs: [
"src/**/*.aidl",
],
path: "src",
}
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
filegroup {
name: "wm_shell-sources-kt",
@@ -98,7 +106,7 @@ android_library {
":wm_shell_protolog_src",
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
":wm_shell-sources-kt",
"src/**/I*.aidl",
":wm_shell-aidls",
],
resource_dirs: [
"res",

View File

@@ -47,21 +47,7 @@ public final class ShellCommandHandlerImpl {
private final ShellExecutor mMainExecutor;
private final HandlerImpl mImpl = new HandlerImpl();
public static ShellCommandHandler create(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
ShellExecutor mainExecutor) {
return new ShellCommandHandlerImpl(shellTaskOrganizer, legacySplitScreenOptional,
splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
appPairsOptional, mainExecutor).mImpl;
}
private ShellCommandHandlerImpl(
public ShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@@ -80,6 +66,10 @@ public final class ShellCommandHandlerImpl {
mMainExecutor = mainExecutor;
}
public ShellCommandHandler asShellCommandHandler() {
return mImpl;
}
/** Dumps WM Shell internal state. */
private void dump(PrintWriter pw) {
mShellTaskOrganizer.dump(pw, "");

View File

@@ -26,7 +26,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -47,44 +47,20 @@ public class ShellInitImpl {
private final FullscreenTaskListener mFullscreenTaskListener;
private final ShellExecutor mMainExecutor;
private final Transitions mTransitions;
private final Optional<StartingSurface> mStartingSurfaceOptional;
private final StartingWindowController mStartingWindow;
private final InitImpl mImpl = new InitImpl();
public static ShellInit create(DisplayImeController displayImeController,
public ShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<StartingSurface> startingSurfaceOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
ShellExecutor mainExecutor) {
return new ShellInitImpl(displayImeController,
dragAndDropController,
shellTaskOrganizer,
legacySplitScreenOptional,
splitScreenOptional,
appPairsOptional,
startingSurfaceOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
transitions,
mainExecutor).mImpl;
}
private ShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<StartingSurface> startingSurfaceOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
StartingWindowController startingWindow,
ShellExecutor mainExecutor) {
mDisplayImeController = displayImeController;
mDragAndDropController = dragAndDropController;
@@ -96,17 +72,21 @@ public class ShellInitImpl {
mPipTouchHandlerOptional = pipTouchHandlerOptional;
mTransitions = transitions;
mMainExecutor = mainExecutor;
mStartingSurfaceOptional = startingSurfaceOptional;
mStartingWindow = startingWindow;
}
public ShellInit asShellInit() {
return mImpl;
}
private void init() {
// Start listening for display changes
mDisplayImeController.startMonitorDisplays();
// Setup the shell organizer
mShellTaskOrganizer.addListenerForType(
mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface);
// Register the shell organizer
mShellTaskOrganizer.initStartingWindow(mStartingWindow);
mShellTaskOrganizer.registerOrganizer();
mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered);

View File

@@ -48,7 +48,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -133,7 +133,7 @@ public class ShellTaskOrganizer extends TaskOrganizer {
private final ArraySet<LocusIdListener> mLocusIdListeners = new ArraySet<>();
private final Object mLock = new Object();
private StartingSurface mStartingSurface;
private StartingWindowController mStartingWindow;
/**
* In charge of showing size compat UI. Can be {@code null} if device doesn't support size
@@ -184,8 +184,8 @@ public class ShellTaskOrganizer extends TaskOrganizer {
/**
* @hide
*/
public void initStartingSurface(StartingSurface startingSurface) {
mStartingSurface = startingSurface;
public void initStartingWindow(StartingWindowController startingWindow) {
mStartingWindow = startingWindow;
}
/**
@@ -302,23 +302,23 @@ public class ShellTaskOrganizer extends TaskOrganizer {
@Override
public void addStartingWindow(StartingWindowInfo info, IBinder appToken) {
if (mStartingSurface != null) {
mStartingSurface.addStartingWindow(info, appToken);
if (mStartingWindow != null) {
mStartingWindow.addStartingWindow(info, appToken);
}
}
@Override
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
if (mStartingSurface != null) {
mStartingSurface.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
if (mStartingWindow != null) {
mStartingWindow.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
}
}
@Override
public void copySplashScreenView(int taskId) {
if (mStartingSurface != null) {
mStartingSurface.copySplashScreenView(taskId);
if (mStartingWindow != null) {
mStartingWindow.copySplashScreenView(taskId);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2021 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;
import android.Manifest;
import android.util.Slog;
import java.util.function.Consumer;
/**
* Helpers for working with executors
*/
public class ExecutorUtils {
/**
* Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
* callback.
*/
public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
String log, Consumer<T> callback) {
executeRemoteCallWithTaskPermission(controllerInstance, log, callback,
false /* blocking */);
}
/**
* Checks that the caller has the MANAGE_ACTIVITY_TASKS permission and executes the given
* callback.
*/
public static <T> void executeRemoteCallWithTaskPermission(RemoteCallable<T> controllerInstance,
String log, Consumer<T> callback, boolean blocking) {
if (controllerInstance == null) return;
final RemoteCallable<T> controller = controllerInstance;
controllerInstance.getContext().enforceCallingPermission(
Manifest.permission.MANAGE_ACTIVITY_TASKS, log);
if (blocking) {
try {
controllerInstance.getRemoteCallExecutor().executeBlocking(() -> {
callback.accept((T) controller);
});
} catch (InterruptedException e) {
Slog.e("ExecutorUtils", "Remote call failed", e);
}
} else {
controllerInstance.getRemoteCallExecutor().execute(() -> {
callback.accept((T) controller);
});
}
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2021 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;
import android.content.Context;
/**
* An interface for controllers that can receive remote calls.
*/
public interface RemoteCallable<T> {
/**
* Returns a context used for permission checking.
*/
Context getContext();
/**
* Returns the executor to post the handler callback to.
*/
ShellExecutor getRemoteCallExecutor();
}

View File

@@ -235,7 +235,7 @@ public class DragAndDropPolicy {
mStarter.startShortcut(packageName, id, stage, position, opts, user);
} else {
mStarter.startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT),
mContext, null, stage, position, opts);
null, stage, position, opts);
}
}
@@ -295,7 +295,7 @@ public class DragAndDropPolicy {
@Nullable Bundle options);
void startShortcut(String packageName, String shortcutId, @StageType int stage,
@StagePosition int position, @Nullable Bundle options, UserHandle user);
void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
void startIntent(PendingIntent intent, Intent fillInIntent,
@StageType int stage, @StagePosition int position,
@Nullable Bundle options);
void enterSplitScreen(int taskId, boolean leftOrTop);
@@ -337,9 +337,8 @@ public class DragAndDropPolicy {
}
@Override
public void startIntent(PendingIntent intent, Context context,
@Nullable Intent fillInIntent, int stage, int position,
@Nullable Bundle options) {
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent, int stage,
int position, @Nullable Bundle options) {
try {
intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2021 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.onehanded;
/**
* Interface that is exposed to remote callers to manipulate the OneHanded feature.
*/
interface IOneHanded {
/**
* Enters one handed mode.
*/
oneway void startOneHanded() = 1;
/**
* Exits one handed mode.
*/
oneway void stopOneHanded() = 2;
}

View File

@@ -26,6 +26,14 @@ import com.android.wm.shell.onehanded.OneHandedGestureHandler.OneHandedGestureEv
*/
@ExternalThread
public interface OneHanded {
/**
* Returns a binder that can be passed to an external process to manipulate OneHanded.
*/
default IOneHanded createExternalInterface() {
return null;
}
/**
* Return one handed settings enabled or not.
*/

View File

@@ -18,6 +18,10 @@ package com.android.wm.shell.onehanded;
import static android.os.UserHandle.USER_CURRENT;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import android.Manifest;
import android.annotation.BinderThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.om.IOverlayManager;
@@ -43,6 +47,8 @@ import com.android.internal.logging.UiEventLogger;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExecutorUtils;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -54,7 +60,7 @@ import java.io.PrintWriter;
/**
* Manages and manipulates the one handed states, transitions, and gesture for phones.
*/
public class OneHandedController {
public class OneHandedController implements RemoteCallable<OneHandedController> {
private static final String TAG = "OneHandedController";
private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE =
@@ -247,6 +253,16 @@ public class OneHandedController {
return mImpl;
}
@Override
public Context getContext() {
return mContext;
}
@Override
public ShellExecutor getRemoteCallExecutor() {
return mMainExecutor;
}
/**
* Set one handed enabled or disabled when user update settings
*/
@@ -576,8 +592,22 @@ public class OneHandedController {
}
}
/**
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
private class OneHandedImpl implements OneHanded {
private IOneHandedImpl mIOneHanded;
@Override
public IOneHanded createExternalInterface() {
if (mIOneHanded != null) {
mIOneHanded.invalidate();
}
mIOneHanded = new IOneHandedImpl(OneHandedController.this);
return mIOneHanded;
}
@Override
public boolean isOneHandedEnabled() {
// This is volatile so return directly
@@ -646,4 +676,39 @@ public class OneHandedController {
});
}
}
/**
* The interface for calls from outside the host process.
*/
@BinderThread
private static class IOneHandedImpl extends IOneHanded.Stub {
private OneHandedController mController;
IOneHandedImpl(OneHandedController controller) {
mController = controller;
}
/**
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
mController = null;
}
@Override
public void startOneHanded() {
executeRemoteCallWithTaskPermission(mController, "startOneHanded",
(controller) -> {
controller.startOneHanded();
});
}
@Override
public void stopOneHanded() {
executeRemoteCallWithTaskPermission(mController, "stopOneHanded",
(controller) -> {
controller.stopOneHanded();
});
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2021 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 android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import com.android.wm.shell.pip.IPipAnimationListener;
/**
* Interface that is exposed to remote callers to manipulate the Pip feature.
*/
interface IPip {
/**
* Notifies that Activity is about to be swiped to home with entering PiP transition and
* queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
*
* @param componentName ComponentName represents the Activity
* @param activityInfo ActivityInfo tied to the Activity
* @param pictureInPictureParams PictureInPictureParams tied to the Activity
* @param launcherRotation Launcher rotation to calculate the PiP destination bounds
* @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
* @return destination bounds the PiP window should land into
*/
Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
in PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) = 1;
/**
* Notifies the swiping Activity to PiP onto home transition is finished
*
* @param componentName ComponentName represents the Activity
* @param destinationBounds the destination bounds the PiP window lands into
*/
oneway void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 2;
/**
* Sets listener to get pinned stack animation callbacks.
*/
oneway void setPinnedStackAnimationListener(IPipAnimationListener listener) = 3;
/**
* Sets the shelf height and visibility.
*/
oneway void setShelfHeight(boolean visible, int shelfHeight) = 4;
}

View File

@@ -14,15 +14,14 @@
* limitations under the License.
*/
package com.android.systemui.shared.recents;
package com.android.wm.shell.pip;
/**
* Listener interface that Launcher attaches to SystemUI to get
* pinned stack animation callbacks.
* Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks.
*/
oneway interface IPinnedStackAnimationListener {
oneway interface IPipAnimationListener {
/**
* Notifies the pinned stack animation is started.
* Notifies the listener that the Pip animation is started.
*/
void onPinnedStackAnimationStarted();
void onPipAnimationStarted();
}

View File

@@ -16,15 +16,10 @@
package com.android.wm.shell.pip;
import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import java.io.PrintWriter;
import java.util.function.Consumer;
@@ -34,6 +29,14 @@ import java.util.function.Consumer;
*/
@ExternalThread
public interface Pip {
/**
* Returns a binder that can be passed to an external process to manipulate PIP.
*/
default IPip createExternalInterface() {
return null;
}
/**
* Expand PIP, it's possible that specific request to activate the window via Alt-tab.
*/
@@ -108,30 +111,6 @@ public interface Pip {
*/
default void showPictureInPictureMenu() {}
/**
* Called by Launcher when swiping an auto-pip enabled Activity to home starts
* @param componentName {@link ComponentName} represents the Activity entering PiP
* @param activityInfo {@link ActivityInfo} tied to the Activity
* @param pictureInPictureParams {@link PictureInPictureParams} tied to the Activity
* @param launcherRotation Rotation Launcher is in
* @param shelfHeight Shelf height when landing PiP window onto Launcher
* @return Destination bounds of PiP window based on the parameters passed in
*/
default Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) {
return null;
}
/**
* Called by Launcher when swiping an auto-pip enable Activity to home finishes
* @param componentName {@link ComponentName} represents the Activity entering PiP
* @param destinationBounds Destination bounds of PiP window
*/
default void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
return;
}
/**
* Called by NavigationBar in order to listen in for PiP bounds change. This is mostly used
* for times where the PiP bounds could conflict with SystemUI elements, such as a stashed

View File

@@ -21,8 +21,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.PictureInPictureParams;
@@ -33,6 +35,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ParceledListSlice;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -44,6 +47,7 @@ import android.view.DisplayInfo;
import android.view.WindowManagerGlobal;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -51,9 +55,13 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ExecutorUtils;
import com.android.wm.shell.common.RemoteCallable;
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.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
@@ -71,7 +79,8 @@ import java.util.function.Consumer;
/**
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
public class PipController implements PipTransitionController.PipTransitionCallback {
public class PipController implements PipTransitionController.PipTransitionCallback,
RemoteCallable<PipController> {
private static final String TAG = "PipController";
private Context mContext;
@@ -85,12 +94,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private PipBoundsState mPipBoundsState;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
protected final PipImpl mImpl = new PipImpl();
protected final PipImpl mImpl;
private final Rect mTmpInsetBounds = new Rect();
private boolean mIsInFixedRotation;
private Consumer<Boolean> mPinnedStackAnimationRecentsCallback;
private IPipAnimationListener mPinnedStackAnimationRecentsCallback;
protected PhonePipMenuController mMenuController;
protected PipTaskOrganizer mPipTaskOrganizer;
@@ -264,6 +273,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
mContext = context;
mImpl = new PipImpl();
mWindowManagerShellWrapper = windowManagerShellWrapper;
mDisplayController = displayController;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
@@ -366,6 +376,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb
});
}
@Override
public Context getContext() {
return mContext;
}
@Override
public ShellExecutor getRemoteCallExecutor() {
return mMainExecutor;
}
private void onConfigurationChanged(Configuration newConfig) {
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
@@ -474,7 +494,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipTaskOrganizer.setOneShotAnimationType(animationType);
}
private void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
private void setPinnedStackAnimationListener(IPipAnimationListener callback) {
mPinnedStackAnimationRecentsCallback = callback;
}
@@ -503,7 +523,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// Disable touches while the animation is running
mTouchHandler.setTouchEnabled(false);
if (mPinnedStackAnimationRecentsCallback != null) {
mPinnedStackAnimationRecentsCallback.accept(true);
try {
mPinnedStackAnimationRecentsCallback.onPipAnimationStarted();
} catch (RemoteException e) {
Log.e(TAG, "Failed to call onPinnedStackAnimationStarted()", e);
}
}
}
@@ -629,7 +653,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipInputConsumer.dump(pw, innerPrefix);
}
/**
* The interface for calls from outside the Shell, within the host process.
*/
private class PipImpl implements Pip {
private IPipImpl mIPip;
@Override
public IPip createExternalInterface() {
if (mIPip != null) {
mIPip.invalidate();
}
mIPip = new IPipImpl(PipController.this);
return mIPip;
}
@Override
public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {
mMainExecutor.execute(() -> {
@@ -686,13 +724,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
});
}
@Override
public void setPinnedStackAnimationListener(Consumer<Boolean> callback) {
mMainExecutor.execute(() -> {
PipController.this.setPinnedStackAnimationListener(callback);
});
}
@Override
public void setPinnedStackAnimationType(int animationType) {
mMainExecutor.execute(() -> {
@@ -714,29 +745,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
});
}
@Override
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams, int launcherRotation,
int shelfHeight) {
Rect[] result = new Rect[1];
try {
mMainExecutor.executeBlocking(() -> {
result[0] = PipController.this.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams, launcherRotation, shelfHeight);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to start swipe pip to home");
}
return result[0];
}
@Override
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
mMainExecutor.execute(() -> {
PipController.this.stopSwipePipToHome(componentName, destinationBounds);
});
}
@Override
public void dump(PrintWriter pw) {
try {
@@ -748,4 +756,89 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
}
}
/**
* The interface for calls from outside the host process.
*/
@BinderThread
private static class IPipImpl extends IPip.Stub {
private PipController mController;
private IPipAnimationListener mListener;
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final PipController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.setPinnedStackAnimationListener(null);
});
}
};
IPipImpl(PipController controller) {
mController = controller;
}
/**
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
mController = null;
}
@Override
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams, int launcherRotation,
int shelfHeight) {
Rect[] result = new Rect[1];
executeRemoteCallWithTaskPermission(mController, "startSwipePipToHome",
(controller) -> {
result[0] = controller.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams, launcherRotation, shelfHeight);
}, true /* blocking */);
return result[0];
}
@Override
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
(controller) -> {
controller.stopSwipePipToHome(componentName, destinationBounds);
});
}
@Override
public void setShelfHeight(boolean visible, int height) {
executeRemoteCallWithTaskPermission(mController, "setShelfHeight",
(controller) -> {
controller.setShelfHeight(visible, height);
});
}
@Override
public void setPinnedStackAnimationListener(IPipAnimationListener listener) {
executeRemoteCallWithTaskPermission(mController, "setPinnedStackAnimationListener",
(controller) -> {
if (mListener != null) {
// Reset the old death recipient
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) {
// Register the death recipient for the new listener to clear the listener
try {
listener.asBinder().linkToDeath(mListenerDeathRecipient,
0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
}
mListener = listener;
controller.setPinnedStackAnimationListener(listener);
});
}
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2021 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.splitscreen;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
/**
* Interface that is exposed to remote callers to manipulate the splitscreen feature.
*/
interface ISplitScreen {
/**
* Registers a split screen listener.
*/
oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1;
/**
* Unregisters a split screen listener.
*/
oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2;
/**
* Hides the side-stage if it is currently visible.
*/
oneway void setSideStageVisibility(boolean visible) = 3;
/**
* Removes a task from the side stage.
*/
oneway void removeFromSideStage(int taskId) = 4;
/**
* Removes the split-screen stages.
*/
oneway void exitSplitScreen() = 5;
/**
* @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible.
*/
oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6;
/**
* Starts a task in a stage.
*/
oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7;
/**
* Starts a shortcut in a stage.
*/
oneway void startShortcut(String packageName, String shortcutId, int stage, int position,
in Bundle options, in UserHandle user) = 8;
/**
* Starts an activity in a stage.
*/
oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage,
int position, in Bundle options) = 9;
}

View File

@@ -14,12 +14,20 @@
* limitations under the License.
*/
package com.android.systemui.shared.recents;
package com.android.wm.shell.splitscreen;
/**
* Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
*/
oneway interface ISplitScreenListener {
/**
* Called when the stage position changes.
*/
void onStagePositionChanged(int stage, int position);
/**
* Called when a task changes stages.
*/
void onTaskStageChanged(int taskId, int stage, boolean visible);
}
}

View File

@@ -35,7 +35,7 @@ import com.android.wm.shell.draganddrop.DragAndDropPolicy;
* TODO: Figure out which of these are actually needed outside of the Shell
*/
@ExternalThread
public interface SplitScreen extends DragAndDropPolicy.Starter {
public interface SplitScreen {
/**
* Stage position isn't specified normally meaning to use what ever it is currently set to.
*/
@@ -89,35 +89,10 @@ public interface SplitScreen extends DragAndDropPolicy.Starter {
void onTaskStageChanged(int taskId, @StageType int stage, boolean visible);
}
/** @return {@code true} if split-screen is currently visible. */
boolean isSplitScreenVisible();
/** Moves a task in the side-stage of split-screen. */
boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition);
/** Moves a task in the side-stage of split-screen. */
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
@StagePosition int sideStagePosition);
/** Removes a task from the side-stage of split-screen. */
boolean removeFromSideStage(int taskId);
/** Sets the position of the side-stage. */
void setSideStagePosition(@StagePosition int sideStagePosition);
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(boolean visible);
/** Removes the split-screen stages. */
void exitSplitScreen();
/** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide);
/** Gets the stage bounds. */
void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
void registerSplitScreenListener(SplitScreenListener listener);
void unregisterSplitScreenListener(SplitScreenListener listener);
void startTask(int taskId,
@StageType int stage, @StagePosition int position, @Nullable Bundle options);
void startShortcut(String packageName, String shortcutId, @StageType int stage,
@StagePosition int position, @Nullable Bundle options, UserHandle user);
void startIntent(PendingIntent intent, Context context,
@Nullable Intent fillInIntent, @StageType int stage,
@StagePosition int position, @Nullable Bundle options);
/**
* Returns a binder that can be passed to an external process to manipulate SplitScreen.
*/
default ISplitScreen createExternalInterface() {
return null;
}
}

View File

@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
@@ -34,19 +35,24 @@ import android.content.Intent;
import android.content.pm.LauncherApps;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
import java.io.PrintWriter;
@@ -55,7 +61,8 @@ import java.io.PrintWriter;
* {@link SplitScreen}.
* @see StageCoordinator
*/
public class SplitScreenController implements DragAndDropPolicy.Starter {
public class SplitScreenController implements DragAndDropPolicy.Starter,
RemoteCallable<SplitScreenController> {
private static final String TAG = SplitScreenController.class.getSimpleName();
private final ShellTaskOrganizer mTaskOrganizer;
@@ -84,6 +91,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
return mImpl;
}
@Override
public Context getContext() {
return mContext;
}
@Override
public ShellExecutor getRemoteCallExecutor() {
return mMainExecutor;
}
public void onOrganizerRegistered() {
if (mStageCoordinator == null) {
// TODO: Multi-display
@@ -172,13 +189,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
}
}
public void startIntent(PendingIntent intent, Context context,
Intent fillInIntent, @SplitScreen.StageType int stage,
@SplitScreen.StagePosition int position, @Nullable Bundle options) {
public void startIntent(PendingIntent intent, Intent fillInIntent,
@SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
@Nullable Bundle options) {
options = resolveStartStage(stage, position, options);
try {
intent.send(context, 0, fillInIntent, null, null, null, options);
intent.send(mContext, 0, fillInIntent, null, null, null, options);
} catch (PendingIntent.CanceledException e) {
Slog.e(TAG, "Failed to launch activity", e);
}
@@ -242,121 +259,170 @@ public class SplitScreenController implements DragAndDropPolicy.Starter {
}
}
/**
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
private class SplitScreenImpl implements SplitScreen {
private ISplitScreenImpl mISplitScreen;
@Override
public boolean isSplitScreenVisible() {
return mMainExecutor.executeBlockingForResult(() -> {
return SplitScreenController.this.isSplitScreenVisible();
}, Boolean.class);
public ISplitScreen createExternalInterface() {
if (mISplitScreen != null) {
mISplitScreen.invalidate();
}
mISplitScreen = new ISplitScreenImpl(SplitScreenController.this);
return mISplitScreen;
}
}
/**
* The interface for calls from outside the host process.
*/
@BinderThread
private static class ISplitScreenImpl extends ISplitScreen.Stub {
private SplitScreenController mController;
private ISplitScreenListener mListener;
private final SplitScreen.SplitScreenListener mSplitScreenListener =
new SplitScreen.SplitScreenListener() {
@Override
public void onStagePositionChanged(int stage, int position) {
try {
if (mListener != null) {
mListener.onStagePositionChanged(stage, position);
}
} catch (RemoteException e) {
Slog.e(TAG, "onStagePositionChanged", e);
}
}
@Override
public void onTaskStageChanged(int taskId, int stage, boolean visible) {
try {
if (mListener != null) {
mListener.onTaskStageChanged(taskId, stage, visible);
}
} catch (RemoteException e) {
Slog.e(TAG, "onTaskStageChanged", e);
}
}
};
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final SplitScreenController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.unregisterSplitScreenListener(mSplitScreenListener);
});
}
};
public ISplitScreenImpl(SplitScreenController controller) {
mController = controller;
}
/**
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
mController = null;
}
@Override
public boolean moveToSideStage(int taskId, int sideStagePosition) {
return mMainExecutor.executeBlockingForResult(() -> {
return SplitScreenController.this.moveToSideStage(taskId, sideStagePosition);
}, Boolean.class);
public void registerSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener",
(controller) -> {
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) {
try {
listener.asBinder().linkToDeath(mListenerDeathRecipient,
0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
}
mListener = listener;
controller.registerSplitScreenListener(mSplitScreenListener);
});
}
@Override
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
int sideStagePosition) {
return mMainExecutor.executeBlockingForResult(() -> {
return SplitScreenController.this.moveToSideStage(task, sideStagePosition);
}, Boolean.class);
}
@Override
public boolean removeFromSideStage(int taskId) {
return mMainExecutor.executeBlockingForResult(() -> {
return SplitScreenController.this.removeFromSideStage(taskId);
}, Boolean.class);
}
@Override
public void setSideStagePosition(int sideStagePosition) {
mMainExecutor.execute(() -> {
SplitScreenController.this.setSideStagePosition(sideStagePosition);
});
}
@Override
public void setSideStageVisibility(boolean visible) {
mMainExecutor.execute(() -> {
SplitScreenController.this.setSideStageVisibility(visible);
});
}
@Override
public void enterSplitScreen(int taskId, boolean leftOrTop) {
mMainExecutor.execute(() -> {
SplitScreenController.this.enterSplitScreen(taskId, leftOrTop);
});
public void unregisterSplitScreenListener(ISplitScreenListener listener) {
executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener",
(controller) -> {
if (mListener != null) {
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
mListener = null;
controller.unregisterSplitScreenListener(mSplitScreenListener);
});
}
@Override
public void exitSplitScreen() {
mMainExecutor.execute(() -> {
SplitScreenController.this.exitSplitScreen();
});
executeRemoteCallWithTaskPermission(mController, "exitSplitScreen",
(controller) -> {
controller.exitSplitScreen();
});
}
@Override
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
mMainExecutor.execute(() -> {
SplitScreenController.this.exitSplitScreenOnHide(exitSplitScreenOnHide);
});
executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide",
(controller) -> {
controller.exitSplitScreenOnHide(exitSplitScreenOnHide);
});
}
@Override
public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) {
try {
mMainExecutor.executeBlocking(() -> {
SplitScreenController.this.getStageBounds(outTopOrLeftBounds,
outBottomOrRightBounds);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to get stage bounds in 2s");
}
public void setSideStageVisibility(boolean visible) {
executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility",
(controller) -> {
controller.setSideStageVisibility(visible);
});
}
@Override
public void registerSplitScreenListener(SplitScreenListener listener) {
mMainExecutor.execute(() -> {
SplitScreenController.this.registerSplitScreenListener(listener);
});
}
@Override
public void unregisterSplitScreenListener(SplitScreenListener listener) {
mMainExecutor.execute(() -> {
SplitScreenController.this.unregisterSplitScreenListener(listener);
});
public void removeFromSideStage(int taskId) {
executeRemoteCallWithTaskPermission(mController, "removeFromSideStage",
(controller) -> {
controller.removeFromSideStage(taskId);
});
}
@Override
public void startTask(int taskId, int stage, int position, @Nullable Bundle options) {
mMainExecutor.execute(() -> {
SplitScreenController.this.startTask(taskId, stage, position, options);
});
executeRemoteCallWithTaskPermission(mController, "startTask",
(controller) -> {
controller.startTask(taskId, stage, position, options);
});
}
@Override
public void startShortcut(String packageName, String shortcutId, int stage, int position,
@Nullable Bundle options, UserHandle user) {
mMainExecutor.execute(() -> {
SplitScreenController.this.startShortcut(packageName, shortcutId, stage, position,
options, user);
});
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
controller.startShortcut(packageName, shortcutId, stage, position,
options, user);
});
}
@Override
public void startIntent(PendingIntent intent, Context context, Intent fillInIntent,
int stage, int position, @Nullable Bundle options) {
mMainExecutor.execute(() -> {
SplitScreenController.this.startIntent(intent, context, fillInIntent, stage,
position, options);
});
public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
@Nullable Bundle options) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
controller.startIntent(intent, fillInIntent, stage, position, options);
});
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 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.startingsurface;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
/**
* Interface that is exposed to remote callers to manipulate starting windows.
*/
interface IStartingWindow {
/**
* Sets listener to get task launching callbacks.
*/
oneway void setStartingWindowListener(IStartingWindowListener listener) = 43;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 The Android Open Source Project
* Copyright (C) 2021 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.systemui.shared.recents;
package com.android.wm.shell.startingsurface;
/**
* Listener interface that Launcher attaches to SystemUI to get

View File

@@ -16,35 +16,15 @@
package com.android.wm.shell.startingsurface;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.StartingWindowInfo;
import java.util.function.BiConsumer;
/**
* Interface to engage starting window feature.
*/
public interface StartingSurface {
/**
* Called when a task need a starting window.
*/
void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken);
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation);
/**
* Called when the Task wants to copy the splash screen.
* @param taskId
*/
void copySplashScreenView(int taskId);
/**
* Registers the starting window listener.
*
* @param listener The callback when need a starting window.
* Returns a binder that can be passed to an external process to manipulate starting windows.
*/
void setStartingWindowListener(BiConsumer<Integer, Integer> listener);
default IStartingWindow createExternalInterface() {
return null;
}
}

View File

@@ -25,6 +25,8 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
@@ -37,6 +39,9 @@ import android.window.StartingWindowInfo;
import android.window.TaskOrganizer;
import android.window.TaskSnapshot;
import androidx.annotation.BinderThread;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
@@ -58,7 +63,7 @@ import java.util.function.BiConsumer;
* constructor to keep everything synchronized.
* @hide
*/
public class StartingWindowController {
public class StartingWindowController implements RemoteCallable<StartingWindowController> {
private static final String TAG = StartingWindowController.class.getSimpleName();
static final boolean DEBUG_SPLASH_SCREEN = false;
static final boolean DEBUG_TASK_SNAPSHOT = false;
@@ -68,17 +73,17 @@ public class StartingWindowController {
private BiConsumer<Integer, Integer> mTaskLaunchingCallback;
private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl();
private final Context mContext;
private final ShellExecutor mSplashScreenExecutor;
// For Car Launcher
public StartingWindowController(Context context, ShellExecutor splashScreenExecutor) {
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor,
new TransactionPool());
mSplashScreenExecutor = splashScreenExecutor;
this(context, splashScreenExecutor, new TransactionPool());
}
public StartingWindowController(Context context, ShellExecutor splashScreenExecutor,
TransactionPool pool) {
mContext = context;
mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, splashScreenExecutor, pool);
mSplashScreenExecutor = splashScreenExecutor;
}
@@ -90,6 +95,16 @@ public class StartingWindowController {
return mImpl;
}
@Override
public Context getContext() {
return mContext;
}
@Override
public ShellExecutor getRemoteCallExecutor() {
return mSplashScreenExecutor;
}
private static class StartingTypeChecker {
TaskSnapshot mSnapshot;
@@ -195,59 +210,121 @@ public class StartingWindowController {
/**
* Called when a task need a starting window.
*/
void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
if (mTaskLaunchingCallback != null) {
mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
}
if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
}
// If prefer don't show, then don't show!
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
mSplashScreenExecutor.execute(() -> {
final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
if (mTaskLaunchingCallback != null) {
mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
}
if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
}
// If prefer don't show, then don't show!
});
}
void copySplashScreenView(int taskId) {
mStartingSurfaceDrawer.copySplashScreenView(taskId);
public void copySplashScreenView(int taskId) {
mSplashScreenExecutor.execute(() -> {
mStartingSurfaceDrawer.copySplashScreenView(taskId);
});
}
/**
* Called when the content of a task is ready to show, starting window can be removed.
*/
void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
mSplashScreenExecutor.execute(() -> {
mStartingSurfaceDrawer.removeStartingWindow(taskId, leash, frame, playRevealAnimation);
});
}
/**
* The interface for calls from outside the Shell, within the host process.
*/
private class StartingSurfaceImpl implements StartingSurface {
private IStartingWindowImpl mIStartingWindow;
@Override
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
mSplashScreenExecutor.execute(() ->
StartingWindowController.this.addStartingWindow(windowInfo, appToken));
public IStartingWindowImpl createExternalInterface() {
if (mIStartingWindow != null) {
mIStartingWindow.invalidate();
}
mIStartingWindow = new IStartingWindowImpl(StartingWindowController.this);
return mIStartingWindow;
}
}
/**
* The interface for calls from outside the host process.
*/
@BinderThread
private static class IStartingWindowImpl extends IStartingWindow.Stub {
private StartingWindowController mController;
private IStartingWindowListener mListener;
private final BiConsumer<Integer, Integer> mStartingWindowListener =
this::notifyIStartingWindowListener;
private final IBinder.DeathRecipient mListenerDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
final StartingWindowController controller = mController;
controller.getRemoteCallExecutor().execute(() -> {
mListener = null;
controller.setStartingWindowListener(null);
});
}
};
public IStartingWindowImpl(StartingWindowController controller) {
mController = controller;
}
/**
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
mController = null;
}
@Override
public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame,
boolean playRevealAnimation) {
mSplashScreenExecutor.execute(() ->
StartingWindowController.this.removeStartingWindow(taskId, leash, frame,
playRevealAnimation));
public void setStartingWindowListener(IStartingWindowListener listener) {
executeRemoteCallWithTaskPermission(mController, "setStartingWindowListener",
(controller) -> {
if (mListener != null) {
// Reset the old death recipient
mListener.asBinder().unlinkToDeath(mListenerDeathRecipient,
0 /* flags */);
}
if (listener != null) {
try {
listener.asBinder().linkToDeath(mListenerDeathRecipient,
0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
}
mListener = listener;
controller.setStartingWindowListener(mStartingWindowListener);
});
}
@Override
public void copySplashScreenView(int taskId) {
mSplashScreenExecutor.execute(() ->
StartingWindowController.this.copySplashScreenView(taskId));
}
private void notifyIStartingWindowListener(int taskId, int supportedType) {
if (mListener == null) {
return;
}
@Override
public void setStartingWindowListener(BiConsumer<Integer, Integer> listener) {
mSplashScreenExecutor.execute(() ->
StartingWindowController.this.setStartingWindowListener(listener));
try {
mListener.onTaskLaunching(taskId, supportedType);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify task launching", e);
}
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2021 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.transition;
import android.window.IRemoteTransition;
import android.window.TransitionFilter;
/**
* Interface that is exposed to remote callers to manipulate the transitions feature.
*/
interface IShellTransitions {
/**
* Registers a remote transition handler.
*/
oneway void registerRemote(in TransitionFilter filter,
in IRemoteTransition remoteTransition) = 1;
/**
* Unregisters a remote transition handler.
*/
oneway void unregisterRemote(in IRemoteTransition remoteTransition) = 2;
}

View File

@@ -16,6 +16,8 @@
package com.android.wm.shell.transition;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -23,6 +25,7 @@ import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
@@ -31,6 +34,8 @@ import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import androidx.annotation.BinderThread;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -42,6 +47,8 @@ import java.util.ArrayList;
* if the request includes a specific remote.
*/
public class RemoteTransitionHandler implements Transitions.TransitionHandler {
private static final String TAG = "RemoteTransitionHandler";
private final ShellExecutor mMainExecutor;
/** Includes remotes explicitly requested by, eg, ActivityOptions */
@@ -51,15 +58,33 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
new ArrayList<>();
private final IBinder.DeathRecipient mTransitionDeathRecipient =
new IBinder.DeathRecipient() {
@Override
@BinderThread
public void binderDied() {
mMainExecutor.execute(() -> {
mFilters.clear();
});
}
};
RemoteTransitionHandler(@NonNull ShellExecutor mainExecutor) {
mMainExecutor = mainExecutor;
}
void addFiltered(TransitionFilter filter, IRemoteTransition remote) {
try {
remote.asBinder().linkToDeath(mTransitionDeathRecipient, 0 /* flags */);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death");
return;
}
mFilters.add(new Pair<>(filter, remote));
}
void removeFiltered(IRemoteTransition remote) {
remote.asBinder().unlinkToDeath(mTransitionDeathRecipient, 0 /* flags */);
for (int i = mFilters.size() - 1; i >= 0; --i) {
if (mFilters.get(i).second == remote) {
mFilters.remove(i);

View File

@@ -26,7 +26,15 @@ import com.android.wm.shell.common.annotations.ExternalThread;
* Interface to manage remote transitions.
*/
@ExternalThread
public interface RemoteTransitions {
public interface ShellTransitions {
/**
* Returns a binder that can be passed to an external process to manipulate remote transitions.
*/
default IShellTransitions createExternalInterface() {
return null;
}
/**
* Registers a remote transition.
*/

View File

@@ -23,6 +23,8 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -51,6 +53,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread;
@@ -60,7 +63,7 @@ import java.util.ArrayList;
import java.util.Arrays;
/** Plays transition animations */
public class Transitions {
public class Transitions implements RemoteCallable<Transitions> {
static final String TAG = "ShellTransitions";
/** Set to {@code true} to enable shell transitions. */
@@ -73,7 +76,7 @@ public class Transitions {
private final ShellExecutor mAnimExecutor;
private final TransitionPlayerImpl mPlayerImpl;
private final RemoteTransitionHandler mRemoteTransitionHandler;
private final RemoteTransitionImpl mImpl = new RemoteTransitionImpl();
private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
/** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
@@ -87,10 +90,6 @@ public class Transitions {
/** Keeps track of currently tracked transitions and all the animations associated with each */
private final ArrayMap<IBinder, ActiveTransition> mActiveTransitions = new ArrayMap<>();
public static RemoteTransitions asRemoteTransitions(Transitions transitions) {
return transitions.mImpl;
}
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
@NonNull ShellExecutor animExecutor) {
@@ -126,6 +125,20 @@ public class Transitions {
mRemoteTransitionHandler = null;
}
public ShellTransitions asRemoteTransitions() {
return mImpl;
}
@Override
public Context getContext() {
return mContext;
}
@Override
public ShellExecutor getRemoteCallExecutor() {
return mMainExecutor;
}
private void dispatchAnimScaleSetting(float scale) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
mHandlers.get(i).setAnimScaleSetting(scale);
@@ -134,8 +147,8 @@ public class Transitions {
/** Create an empty/non-registering transitions object for system-ui tests. */
@VisibleForTesting
public static RemoteTransitions createEmptyForTesting() {
return new RemoteTransitions() {
public static ShellTransitions createEmptyForTesting() {
return new ShellTransitions() {
@Override
public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
@androidx.annotation.NonNull IRemoteTransition remoteTransition) {
@@ -426,24 +439,74 @@ public class Transitions {
}
}
/**
* The interface for calls from outside the Shell, within the host process.
*/
@ExternalThread
private class RemoteTransitionImpl implements RemoteTransitions {
private class ShellTransitionImpl implements ShellTransitions {
private IShellTransitionsImpl mIShellTransitions;
@Override
public IShellTransitions createExternalInterface() {
if (mIShellTransitions != null) {
mIShellTransitions.invalidate();
}
mIShellTransitions = new IShellTransitionsImpl(Transitions.this);
return mIShellTransitions;
}
@Override
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull IRemoteTransition remoteTransition) {
mMainExecutor.execute(() -> {
Transitions.this.registerRemote(filter, remoteTransition);
mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
});
}
@Override
public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
mMainExecutor.execute(() -> {
Transitions.this.unregisterRemote(remoteTransition);
mRemoteTransitionHandler.removeFiltered(remoteTransition);
});
}
}
/**
* The interface for calls from outside the host process.
*/
@BinderThread
private static class IShellTransitionsImpl extends IShellTransitions.Stub {
private Transitions mTransitions;
IShellTransitionsImpl(Transitions transitions) {
mTransitions = transitions;
}
/**
* Invalidates this instance, preventing future calls from updating the controller.
*/
void invalidate() {
mTransitions = null;
}
@Override
public void registerRemote(@NonNull TransitionFilter filter,
@NonNull IRemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mTransitions, "registerRemote",
(transitions) -> {
transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
});
}
@Override
public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote",
(transitions) -> {
transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
});
}
}
private class SettingsObserver extends ContentObserver {
SettingsObserver() {

View File

@@ -205,7 +205,7 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -217,12 +217,12 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -234,12 +234,12 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -251,7 +251,7 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -263,7 +263,7 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
}
@@ -276,13 +276,13 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}
@@ -295,13 +295,13 @@ public class DragAndDropPolicyTest {
mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any());
reset(mSplitScreenStarter);
// TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
verify(mSplitScreenStarter).startIntent(any(), any(), any(),
verify(mSplitScreenStarter).startIntent(any(), any(),
eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any());
}

View File

@@ -41,6 +41,7 @@ android_library {
srcs: [
"src/**/*.java",
"src/**/I*.aidl",
":wm_shell-aidls",
],
static_libs: [

View File

@@ -16,11 +16,6 @@
package com.android.systemui.shared.recents;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -28,25 +23,14 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.view.MotionEvent;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.ISplitScreenListener;
import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
/**
* Temporary callbacks into SystemUI.
* Next id = 44
*/
interface ISystemUiProxy {
/**
* Proxies SurfaceControl.screenshotToBuffer().
* @Removed
* GraphicBufferCompat screenshot(in Rect sourceCrop, int width, int height, int minLayer,
* int maxLayer, boolean useIdentityTransform, int rotation) = 0;
*/
/**
* Begins screen pinning on the provided {@param taskId}.
*/
@@ -114,11 +98,6 @@ interface ISystemUiProxy {
*/
void stopScreenPinning() = 17;
/**
* Sets the shelf height and visibility.
*/
void setShelfHeight(boolean visible, int shelfHeight) = 20;
/**
* Handle the provided image as if it was a screenshot.
*
@@ -138,27 +117,12 @@ interface ISystemUiProxy {
*/
void notifySwipeToHomeFinished() = 23;
/**
* Sets listener to get pinned stack animation callbacks.
*/
void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) = 24;
/**
* Notifies that quickstep will switch to a new task
* @param rotation indicates which Surface.Rotation the gesture was started in
*/
void onQuickSwitchToNewTask(int rotation) = 25;
/**
* Start the one-handed mode.
*/
void startOneHandedMode() = 26;
/**
* Stop the one-handed mode.
*/
void stopOneHandedMode() = 27;
/**
* Handle the provided image as if it was a screenshot.
*/
@@ -170,88 +134,5 @@ interface ISystemUiProxy {
*/
void expandNotificationPanel() = 29;
/**
* Notifies that Activity is about to be swiped to home with entering PiP transition and
* queries the destination bounds for PiP depends on Launcher's rotation and shelf height.
*
* @param componentName ComponentName represents the Activity
* @param activityInfo ActivityInfo tied to the Activity
* @param pictureInPictureParams PictureInPictureParams tied to the Activity
* @param launcherRotation Launcher rotation to calculate the PiP destination bounds
* @param shelfHeight Shelf height of launcher to calculate the PiP destination bounds
* @return destination bounds the PiP window should land into
*/
Rect startSwipePipToHome(in ComponentName componentName, in ActivityInfo activityInfo,
in PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) = 30;
/**
* Notifies the swiping Activity to PiP onto home transition is finished
*
* @param componentName ComponentName represents the Activity
* @param destinationBounds the destination bounds the PiP window lands into
*/
void stopSwipePipToHome(in ComponentName componentName, in Rect destinationBounds) = 31;
/**
* Registers a RemoteTransitionCompat that will handle transitions. This parameter bundles an
* IRemoteTransition and a filter that must pass for it.
*/
void registerRemoteTransition(in RemoteTransitionCompat remoteTransition) = 32;
/** Unegisters a RemoteTransitionCompat that will handle transitions. */
void unregisterRemoteTransition(in RemoteTransitionCompat remoteTransition) = 33;
// SplitScreen APIs...copied from SplitScreen.java
/**
* Stage position isn't specified normally meaning to use what ever it is currently set to.
*/
//int STAGE_POSITION_UNDEFINED = -1;
/**
* Specifies that a stage is positioned at the top half of the screen if
* in portrait mode or at the left half of the screen if in landscape mode.
*/
//int STAGE_POSITION_TOP_OR_LEFT = 0;
/**
* Specifies that a stage is positioned at the bottom half of the screen if
* in portrait mode or at the right half of the screen if in landscape mode.
*/
//int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
/**
* Stage type isn't specified normally meaning to use what ever the default is.
* E.g. exit split-screen and launch the app in fullscreen.
*/
//int STAGE_TYPE_UNDEFINED = -1;
/**
* The main stage type.
* @see MainStage
*/
//int STAGE_TYPE_MAIN = 0;
/**
* The side stage type.
* @see SideStage
*/
//int STAGE_TYPE_SIDE = 1;
void registerSplitScreenListener(in ISplitScreenListener listener) = 34;
void unregisterSplitScreenListener(in ISplitScreenListener listener) = 35;
/** Hides the side-stage if it is currently visible. */
void setSideStageVisibility(in boolean visible) = 36;
/** Removes the split-screen stages. */
void exitSplitScreen() = 37;
/** @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. */
void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 38;
void startTask(in int taskId, in int stage, in int position, in Bundle options) = 39;
void startShortcut(in String packageName, in String shortcutId, in int stage, in int position,
in Bundle options, in UserHandle user) = 40;
void startIntent(
in PendingIntent intent, in Intent fillInIntent, in int stage, in int position,
in Bundle options) = 41;
void removeFromSideStage(in int taskId) = 42;
/**
* Sets listener to get task launching callbacks.
*/
void setStartingWindowListener(IStartingWindowListener listener) = 43;
// Next id = 44
}

View File

@@ -41,6 +41,18 @@ public class QuickStepContract {
public static final String KEY_EXTRA_INPUT_MONITOR = "extra_input_monitor";
public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners";
// See IPip.aidl
public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
// See ISplitScreen.aidl
public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
// See IOneHanded.aidl
public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed";
// See IShellTransitions.aidl
public static final String KEY_EXTRA_SHELL_SHELL_TRANSITIONS =
"extra_shell_shell_transitions";
// See IStartingWindow.aidl
public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
"extra_shell_starting_window";
public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;

View File

@@ -34,7 +34,7 @@ import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.ShellTransitions;
import java.util.Optional;
@@ -87,7 +87,7 @@ public interface SysUIComponent {
Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump);
@BindsInstance
Builder setTransitions(RemoteTransitions t);
Builder setTransitions(ShellTransitions t);
@BindsInstance
Builder setStartingSurface(Optional<StartingSurface> s);

View File

@@ -33,7 +33,7 @@ import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.ShellTransitions;
import java.util.Optional;
@@ -98,7 +98,7 @@ public interface WMComponent {
Optional<TaskViewFactory> getTaskViewFactory();
@WMSingleton
RemoteTransitions getTransitions();
ShellTransitions getTransitions();
@WMSingleton
Optional<StartingSurface> getStartingSurface();

View File

@@ -25,6 +25,11 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
@@ -35,15 +40,12 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_T
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -57,14 +59,12 @@ import android.os.Looper;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Log;
import android.view.InputMonitor;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.accessibility.AccessibilityManager;
import android.window.IRemoteTransition;
import androidx.annotation.NonNull;
@@ -83,15 +83,11 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.ISplitScreenListener;
import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -103,7 +99,7 @@ import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.ShellTransitions;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -111,7 +107,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -151,12 +146,11 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final ScreenshotHelper mScreenshotHelper;
private final Optional<OneHanded> mOneHandedOptional;
private final CommandQueue mCommandQueue;
private final RemoteTransitions mShellTransitions;
private final ShellTransitions mShellTransitions;
private final Optional<StartingSurface> mStartingSurface;
private Region mActiveNavBarRegion;
private IPinnedStackAnimationListener mIPinnedStackAnimationListener;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private boolean mBound;
@@ -169,8 +163,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
private float mWindowCornerRadius;
private boolean mSupportsRoundedCornersOnWindows;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private final ArraySet<IRemoteTransition> mRemoteTransitions = new ArraySet<>();
private IStartingWindowListener mIStartingWindowListener;
@VisibleForTesting
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -382,20 +374,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
@Override
public void setShelfHeight(boolean visible, int shelfHeight) {
if (!verifyCaller("setShelfHeight")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mPipOptional.ifPresent(
pip -> pip.setShelfHeight(visible, shelfHeight));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
Insets visibleInsets, int taskId) {
@@ -423,36 +401,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
@Override
public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) {
if (!verifyCaller("setPinnedStackAnimationListener")) {
return;
}
mIPinnedStackAnimationListener = listener;
final long token = Binder.clearCallingIdentity();
try {
mPipOptional.ifPresent(
pip -> pip.setPinnedStackAnimationListener(mPinnedStackAnimationCallback));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setStartingWindowListener(IStartingWindowListener listener) {
if (!verifyCaller("setStartingWindowListener")) {
return;
}
mIStartingWindowListener = listener;
final long token = Binder.clearCallingIdentity();
try {
mStartingSurface.ifPresent(s ->
s.setStartingWindowListener(mStartingWindowListener));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onQuickSwitchToNewTask(@Surface.Rotation int rotation) {
if (!verifyCaller("onQuickSwitchToNewTask")) {
@@ -466,32 +414,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
@Override
public void startOneHandedMode() {
if (!verifyCaller("startOneHandedMode")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mOneHandedOptional.ifPresent(oneHanded -> oneHanded.startOneHanded());
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void stopOneHandedMode() {
if (!verifyCaller("stopOneHandedMode")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mOneHandedOptional.ifPresent(oneHanded -> oneHanded.stopOneHanded());
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void handleImageBundleAsScreenshot(Bundle screenImageBundle, Rect locationInScreen,
Insets visibleInsets, Task.TaskKey task) {
@@ -520,190 +442,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
@Override
public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
PictureInPictureParams pictureInPictureParams,
int launcherRotation, int shelfHeight) {
if (!verifyCaller("startSwipePipToHome")) {
return null;
}
final long binderToken = Binder.clearCallingIdentity();
try {
return mPipOptional.map(pip ->
pip.startSwipePipToHome(componentName, activityInfo,
pictureInPictureParams, launcherRotation, shelfHeight))
.orElse(null);
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
@Override
public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
if (!verifyCaller("stopSwipePipToHome")) {
return;
}
final long binderToken = Binder.clearCallingIdentity();
try {
mPipOptional.ifPresent(pip -> pip.stopSwipePipToHome(
componentName, destinationBounds));
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
@Override
public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
if (!verifyCaller("registerRemoteTransition")) return;
final long binderToken = Binder.clearCallingIdentity();
try {
mRemoteTransitions.add(remoteTransition.getTransition());
mShellTransitions.registerRemote(
remoteTransition.getFilter(), remoteTransition.getTransition());
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
@Override
public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
if (!verifyCaller("registerRemoteTransition")) return;
final long binderToken = Binder.clearCallingIdentity();
try {
mRemoteTransitions.remove(remoteTransition.getTransition());
mShellTransitions.unregisterRemote(remoteTransition.getTransition());
} finally {
Binder.restoreCallingIdentity(binderToken);
}
}
@Override
public void registerSplitScreenListener(ISplitScreenListener listener) {
if (!verifyCaller("registerSplitScreenListener")) {
return;
}
mISplitScreenListener = listener;
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(
s -> s.registerSplitScreenListener(mSplitScreenListener));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void unregisterSplitScreenListener(ISplitScreenListener listener) {
if (!verifyCaller("unregisterSplitScreenListener")) {
return;
}
mISplitScreenListener = null;
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(
s -> s.unregisterSplitScreenListener(mSplitScreenListener));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setSideStageVisibility(boolean visible) {
if (!verifyCaller("setSideStageVisibility")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(s -> s.setSideStageVisibility(visible));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void exitSplitScreen() {
if (!verifyCaller("exitSplitScreen")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(s -> s.exitSplitScreen());
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
if (!verifyCaller("exitSplitScreenOnHide")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(s -> s.exitSplitScreenOnHide(exitSplitScreenOnHide));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void startTask(int taskId, int stage, int position, Bundle options) {
if (!verifyCaller("startTask")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(
s -> s.startTask(taskId, stage, position, options));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void startShortcut(String packageName, String shortcutId, int stage, int position,
Bundle options, UserHandle user) {
if (!verifyCaller("startShortcut")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(s ->
s.startShortcut(packageName, shortcutId, stage, position, options, user));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void startIntent(PendingIntent intent, Intent fillInIntent,
int stage, int position, Bundle options) {
if (!verifyCaller("startIntent")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(s ->
s.startIntent(intent, mContext, fillInIntent, stage, position, options));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void removeFromSideStage(int taskId) {
if (!verifyCaller("removeFromSideStage")) {
return;
}
final long token = Binder.clearCallingIdentity();
try {
mSplitScreenOptional.ifPresent(
s -> s.removeFromSideStage(taskId));
} finally {
Binder.restoreCallingIdentity(token);
}
}
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -757,6 +495,22 @@ public class OverviewProxyService extends CurrentUserTracker implements
params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
mPipOptional.ifPresent((pip) -> params.putBinder(
KEY_EXTRA_SHELL_PIP,
pip.createExternalInterface().asBinder()));
mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder(
KEY_EXTRA_SHELL_SPLIT_SCREEN,
splitscreen.createExternalInterface().asBinder()));
mOneHandedOptional.ifPresent((onehanded) -> params.putBinder(
KEY_EXTRA_SHELL_ONE_HANDED,
onehanded.createExternalInterface().asBinder()));
params.putBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS,
mShellTransitions.createExternalInterface().asBinder());
mStartingSurface.ifPresent((startingwindow) -> params.putBinder(
KEY_EXTRA_SHELL_STARTING_WINDOW,
startingwindow.createExternalInterface().asBinder()));
try {
mOverviewProxy.onInitialize(params);
} catch (RemoteException e) {
@@ -796,42 +550,11 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
this::notifySplitScreenBoundsChanged;
private final Consumer<Boolean> mPinnedStackAnimationCallback =
this::notifyPinnedStackAnimationStarted;
private final BiConsumer<Integer, Integer> mStartingWindowListener =
this::notifyTaskLaunching;
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
private ISplitScreenListener mISplitScreenListener;
private final SplitScreen.SplitScreenListener mSplitScreenListener =
new SplitScreen.SplitScreenListener() {
@Override
public void onStagePositionChanged(int stage, int position) {
try {
if (mISplitScreenListener != null) {
mISplitScreenListener.onStagePositionChanged(stage, position);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "onStagePositionChanged", e);
}
}
@Override
public void onTaskStageChanged(int taskId, int stage, boolean visible) {
try {
if (mISplitScreenListener != null) {
mISplitScreenListener.onTaskStageChanged(taskId, stage, visible);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "onTaskStageChanged", e);
}
}
};
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
@@ -844,7 +567,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
Optional<Lazy<StatusBar>> statusBarOptionalLazy,
Optional<OneHanded> oneHandedOptional,
BroadcastDispatcher broadcastDispatcher,
RemoteTransitions shellTransitions,
ShellTransitions shellTransitions,
Optional<StartingSurface> startingSurface) {
super(broadcastDispatcher);
mContext = context;
@@ -961,29 +684,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
private void notifyPinnedStackAnimationStarted(Boolean isAnimationStarted) {
if (mIPinnedStackAnimationListener == null) {
return;
}
try {
mIPinnedStackAnimationListener.onPinnedStackAnimationStarted();
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onPinnedStackAnimationStarted()", e);
}
}
private void notifyTaskLaunching(int taskId, int supportedType) {
if (mIStartingWindowListener == null) {
return;
}
try {
mIStartingWindowListener.onTaskLaunching(taskId, supportedType);
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call notifyTaskLaunching()", e);
}
}
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean bouncerShowing) {
mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
@@ -1027,12 +727,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
// Clean up the minimized state if launcher dies
mLegacySplitScreenOptional.ifPresent(
splitScreen -> splitScreen.setMinimized(false));
// Clean up any registered remote transitions
for (int i = mRemoteTransitions.size() - 1; i >= 0; --i) {
mShellTransitions.unregisterRemote(mRemoteTransitions.valueAt(i));
}
mRemoteTransitions.clear();
}
public void startConnectionToCurrentUser() {

View File

@@ -81,7 +81,7 @@ import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.transition.RemoteTransitions;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -399,8 +399,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static RemoteTransitions provideRemoteTransitions(Transitions transitions) {
return Transitions.asRemoteTransitions(transitions);
static ShellTransitions provideRemoteTransitions(Transitions transitions) {
return transitions.asRemoteTransitions();
}
@WMSingleton
@@ -509,27 +509,33 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static ShellInit provideShellInit(DisplayImeController displayImeController,
static ShellInit provideShellInit(ShellInitImpl impl) {
return impl.asShellInit();
}
@WMSingleton
@Provides
static ShellInitImpl provideShellInitImpl(DisplayImeController displayImeController,
DragAndDropController dragAndDropController,
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<StartingSurface> startingSurface,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Transitions transitions,
StartingWindowController startingWindow,
@ShellMainThread ShellExecutor mainExecutor) {
return ShellInitImpl.create(displayImeController,
return new ShellInitImpl(displayImeController,
dragAndDropController,
shellTaskOrganizer,
legacySplitScreenOptional,
splitScreenOptional,
appPairsOptional,
startingSurface,
pipTouchHandlerOptional,
fullscreenTaskListener,
transitions,
startingWindow,
mainExecutor);
}
@@ -539,7 +545,13 @@ public abstract class WMShellBaseModule {
*/
@WMSingleton
@Provides
static Optional<ShellCommandHandler> provideShellCommandHandler(
static Optional<ShellCommandHandler> provideShellCommandHandler(ShellCommandHandlerImpl impl) {
return Optional.of(impl.asShellCommandHandler());
}
@WMSingleton
@Provides
static ShellCommandHandlerImpl provideShellCommandHandlerImpl(
ShellTaskOrganizer shellTaskOrganizer,
Optional<LegacySplitScreenController> legacySplitScreenOptional,
Optional<SplitScreenController> splitScreenOptional,
@@ -548,8 +560,8 @@ public abstract class WMShellBaseModule {
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.of(ShellCommandHandlerImpl.create(shellTaskOrganizer,
return new ShellCommandHandlerImpl(shellTaskOrganizer,
legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
hideDisplayCutout, appPairsOptional, mainExecutor));
hideDisplayCutout, appPairsOptional, mainExecutor);
}
}

View File

@@ -1,123 +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.systemui.recents;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.transition.RemoteTransitions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Optional;
import dagger.Lazy;
/**
* Unit tests for {@link com.android.systemui.recents.OverviewProxyService}
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class OverviewProxyServiceTest extends SysuiTestCase {
private OverviewProxyService mSpiedOverviewProxyService;
private TestableContext mSpiedContext;
@Mock private BroadcastDispatcher mMockBroadcastDispatcher;
@Mock private CommandQueue mMockCommandQueue;
@Mock private Lazy<NavigationBarController> mMockNavBarControllerLazy;
@Mock private IPinnedStackAnimationListener mMockPinnedStackAnimationListener;
@Mock private NavigationModeController mMockNavModeController;
@Mock private NotificationShadeWindowController mMockStatusBarWinController;
@Mock private Optional<Pip> mMockPipOptional;
@Mock private Optional<LegacySplitScreen> mMockLegacySplitScreenOptional;
@Mock private Optional<SplitScreen> mMockSplitScreenOptional;
@Mock private Optional<Lazy<StatusBar>> mMockStatusBarOptionalLazy;
@Mock private Optional<com.android.wm.shell.onehanded.OneHanded> mMockOneHandedOptional;
@Mock private PackageManager mPackageManager;
@Mock private SysUiState mMockSysUiState;
@Mock private RemoteTransitions mMockTransitions;
@Mock private Optional<StartingSurface> mStartingSurface;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
mSpiedOverviewProxyService = spy(new OverviewProxyService(mSpiedContext, mMockCommandQueue,
mMockNavBarControllerLazy, mMockNavModeController, mMockStatusBarWinController,
mMockSysUiState, mMockPipOptional, mMockLegacySplitScreenOptional,
mMockSplitScreenOptional, mMockStatusBarOptionalLazy, mMockOneHandedOptional,
mMockBroadcastDispatcher, mMockTransitions, mStartingSurface));
}
@Test
public void testNonPipDevice_shouldNotNotifySwipeToHomeFinished() throws RemoteException {
mSpiedOverviewProxyService.mSysUiProxy.notifySwipeToHomeFinished();
verify(mMockPipOptional, never()).ifPresent(any());
}
@Test
public void testNonPipDevice_shouldNotSetPinnedStackAnimationListener() throws RemoteException {
mSpiedOverviewProxyService.mSysUiProxy.setPinnedStackAnimationListener(
mMockPinnedStackAnimationListener);
verify(mMockPipOptional, never()).ifPresent(any());
}
@Test
public void testNonPipDevice_shouldNotSetShelfHeight() throws RemoteException {
mSpiedOverviewProxyService.mSysUiProxy.setShelfHeight(true /* visible */,
100 /* shelfHeight */);
verify(mMockPipOptional, never()).ifPresent(any());
}
}