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:
@@ -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",
|
||||
|
||||
@@ -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, "");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ android_library {
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/I*.aidl",
|
||||
":wm_shell-aidls",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user