Support starting a shortcut into multi-instances split
Support appending MULTIPLE_TASK flag when starting a shotcut into multi-instances split. Align the fallback logic of canceling split screen from a shortcut and an intent to prevent broken split screen. Bug: 265244780 Test: atest WMShellUnitTests Test: atest WMShellFlickerTests Test: verify appended flag from log with the repro steps of the bug Change-Id: I5942eac71389bcbbef55074fbf127470439fc1e8
This commit is contained in:
@@ -699,19 +699,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
return bounds.width() > bounds.height();
|
||||
}
|
||||
|
||||
/** Reverse the split position. */
|
||||
@SplitPosition
|
||||
public static int reversePosition(@SplitPosition int position) {
|
||||
switch (position) {
|
||||
case SPLIT_POSITION_TOP_OR_LEFT:
|
||||
return SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
case SPLIT_POSITION_BOTTOM_OR_RIGHT:
|
||||
return SPLIT_POSITION_TOP_OR_LEFT;
|
||||
default:
|
||||
return SPLIT_POSITION_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if this layout is landscape.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.common.split;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
|
||||
/** Helper utility class for split screen components to use. */
|
||||
public class SplitScreenUtils {
|
||||
/** Reverse the split position. */
|
||||
@SplitScreenConstants.SplitPosition
|
||||
public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) {
|
||||
switch (position) {
|
||||
case SPLIT_POSITION_TOP_OR_LEFT:
|
||||
return SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
case SPLIT_POSITION_BOTTOM_OR_RIGHT:
|
||||
return SPLIT_POSITION_TOP_OR_LEFT;
|
||||
case SPLIT_POSITION_UNDEFINED:
|
||||
default:
|
||||
return SPLIT_POSITION_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if the task is valid for split screen. */
|
||||
public static boolean isValidToSplit(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
return taskInfo != null && taskInfo.supportsMultiWindow
|
||||
&& ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
|
||||
&& ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
|
||||
}
|
||||
|
||||
/** Retrieve package name from an intent */
|
||||
@Nullable
|
||||
public static String getPackageName(Intent intent) {
|
||||
if (intent == null || intent.getComponent() == null) {
|
||||
return null;
|
||||
}
|
||||
return intent.getComponent().getPackageName();
|
||||
}
|
||||
|
||||
/** Retrieve package name from a PendingIntent */
|
||||
@Nullable
|
||||
public static String getPackageName(PendingIntent pendingIntent) {
|
||||
if (pendingIntent == null || pendingIntent.getIntent() == null) {
|
||||
return null;
|
||||
}
|
||||
return getPackageName(pendingIntent.getIntent());
|
||||
}
|
||||
|
||||
/** Retrieve package name from a taskId */
|
||||
@Nullable
|
||||
public static String getPackageName(int taskId, ShellTaskOrganizer taskOrganizer) {
|
||||
final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
|
||||
return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
|
||||
}
|
||||
|
||||
/** Returns true if they are the same package. */
|
||||
public static boolean samePackage(String packageName1, String packageName2) {
|
||||
return packageName1 != null && packageName1.equals(packageName2);
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
|
||||
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
|
||||
import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
|
||||
@@ -39,11 +42,8 @@ import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
@@ -82,8 +82,8 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
import com.android.wm.shell.common.split.SplitLayout;
|
||||
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
|
||||
import com.android.wm.shell.common.split.SplitScreenUtils;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
@@ -318,10 +318,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
return mStageCoordinator;
|
||||
}
|
||||
|
||||
public boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
|
||||
return mStageCoordinator.isValidToEnterSplitScreen(taskInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
|
||||
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
|
||||
@@ -480,39 +476,54 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
@Override
|
||||
public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
|
||||
@Nullable Bundle options, UserHandle user) {
|
||||
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
|
||||
@Override
|
||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
||||
RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers,
|
||||
RemoteAnimationTarget[] nonApps,
|
||||
final IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
|
||||
}
|
||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||
mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
|
||||
mSyncQueue.queue(evictWct);
|
||||
if (options == null) options = new Bundle();
|
||||
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
|
||||
|
||||
if (samePackage(packageName, getPackageName(reverseSplitPosition(position)))) {
|
||||
if (supportMultiInstancesSplit(packageName)) {
|
||||
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||
} else if (isSplitScreenVisible()) {
|
||||
mStageCoordinator.switchSplitPosition("startShortcut");
|
||||
return;
|
||||
} else {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
|
||||
"Cancel entering split as not supporting multi-instances");
|
||||
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void onAnimationCancelled(boolean isKeyguardOccluded) {
|
||||
}
|
||||
};
|
||||
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
|
||||
null /* wct */);
|
||||
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
|
||||
0 /* duration */, 0 /* statusBarTransitionDelay */);
|
||||
ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
|
||||
activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
|
||||
try {
|
||||
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
|
||||
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
|
||||
activityOptions.toBundle(), user);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Slog.e(TAG, "Failed to launch shortcut", e);
|
||||
}
|
||||
|
||||
mStageCoordinator.startShortcut(packageName, shortcutId, position,
|
||||
activityOptions.toBundle(), user);
|
||||
}
|
||||
|
||||
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
|
||||
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
|
||||
@SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
|
||||
InstanceId instanceId) {
|
||||
if (options1 == null) options1 = new Bundle();
|
||||
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
|
||||
|
||||
final String packageName1 = shortcutInfo.getPackage();
|
||||
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
|
||||
if (samePackage(packageName1, packageName2)) {
|
||||
if (supportMultiInstancesSplit(shortcutInfo.getPackage())) {
|
||||
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||
} else {
|
||||
taskId = INVALID_TASK_ID;
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
|
||||
"Cancel entering split as not supporting multi-instances");
|
||||
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
|
||||
activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
|
||||
instanceId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -530,8 +541,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
@SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
|
||||
InstanceId instanceId) {
|
||||
Intent fillInIntent = null;
|
||||
if (launchSameAppAdjacently(pendingIntent, taskId)) {
|
||||
if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
|
||||
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
|
||||
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
|
||||
if (samePackage(packageName1, packageName2)) {
|
||||
if (supportMultiInstancesSplit(packageName1)) {
|
||||
fillInIntent = new Intent();
|
||||
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||
@@ -551,8 +564,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
|
||||
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
|
||||
Intent fillInIntent = null;
|
||||
if (launchSameAppAdjacently(pendingIntent, taskId)) {
|
||||
if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
|
||||
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
|
||||
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
|
||||
if (samePackage(packageName1, packageName2)) {
|
||||
if (supportMultiInstancesSplit(packageName1)) {
|
||||
fillInIntent = new Intent();
|
||||
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||
@@ -573,8 +588,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
|
||||
Intent fillInIntent1 = null;
|
||||
Intent fillInIntent2 = null;
|
||||
if (launchSameAppAdjacently(pendingIntent1, pendingIntent2)) {
|
||||
if (supportMultiInstancesSplit(pendingIntent1.getIntent().getComponent())) {
|
||||
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
|
||||
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
|
||||
if (samePackage(packageName1, packageName2)) {
|
||||
if (supportMultiInstancesSplit(packageName1)) {
|
||||
fillInIntent1 = new Intent();
|
||||
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||
fillInIntent2 = new Intent();
|
||||
@@ -602,13 +619,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
if (fillInIntent == null) fillInIntent = new Intent();
|
||||
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
||||
|
||||
if (launchSameAppAdjacently(position, intent)) {
|
||||
final ComponentName launching = intent.getIntent().getComponent();
|
||||
if (supportMultiInstancesSplit(launching)) {
|
||||
final String packageName1 = SplitScreenUtils.getPackageName(intent);
|
||||
final String packageName2 = getPackageName(reverseSplitPosition(position));
|
||||
if (SplitScreenUtils.samePackage(packageName1, packageName2)) {
|
||||
if (supportMultiInstancesSplit(packageName1)) {
|
||||
// To prevent accumulating large number of instances in the background, reuse task
|
||||
// in the background with priority.
|
||||
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
|
||||
.map(recentTasks -> recentTasks.findTaskInBackground(launching))
|
||||
.map(recentTasks -> recentTasks.findTaskInBackground(
|
||||
intent.getIntent().getComponent()))
|
||||
.orElse(null);
|
||||
if (taskInfo != null) {
|
||||
startTask(taskInfo.taskId, position, options);
|
||||
@@ -636,63 +655,32 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
|
||||
}
|
||||
|
||||
/** Retrieve package name of a specific split position if split screen is activated, otherwise
|
||||
* returns the package name of the top running task. */
|
||||
@Nullable
|
||||
private String getPackageName(Intent intent) {
|
||||
if (intent == null || intent.getComponent() == null) {
|
||||
return null;
|
||||
}
|
||||
return intent.getComponent().getPackageName();
|
||||
}
|
||||
|
||||
private boolean launchSameAppAdjacently(@SplitPosition int position,
|
||||
PendingIntent pendingIntent) {
|
||||
ActivityManager.RunningTaskInfo adjacentTaskInfo = null;
|
||||
private String getPackageName(@SplitPosition int position) {
|
||||
ActivityManager.RunningTaskInfo taskInfo;
|
||||
if (isSplitScreenVisible()) {
|
||||
adjacentTaskInfo = getTaskInfo(SplitLayout.reversePosition(position));
|
||||
taskInfo = getTaskInfo(position);
|
||||
} else {
|
||||
adjacentTaskInfo = mRecentTasksOptional
|
||||
.map(recentTasks -> recentTasks.getTopRunningTask()).orElse(null);
|
||||
if (!isValidToEnterSplitScreen(adjacentTaskInfo)) {
|
||||
return false;
|
||||
taskInfo = mRecentTasksOptional
|
||||
.map(recentTasks -> recentTasks.getTopRunningTask())
|
||||
.orElse(null);
|
||||
if (!isValidToSplit(taskInfo)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (adjacentTaskInfo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String targetPackageName = getPackageName(pendingIntent.getIntent());
|
||||
final String adjacentPackageName = getPackageName(adjacentTaskInfo.baseIntent);
|
||||
return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
|
||||
}
|
||||
|
||||
private boolean launchSameAppAdjacently(PendingIntent pendingIntent, int taskId) {
|
||||
final ActivityManager.RunningTaskInfo adjacentTaskInfo =
|
||||
mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||
if (adjacentTaskInfo == null) {
|
||||
return false;
|
||||
}
|
||||
final String targetPackageName = getPackageName(pendingIntent.getIntent());
|
||||
final String adjacentPackageName = getPackageName(adjacentTaskInfo.baseIntent);
|
||||
return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
|
||||
}
|
||||
|
||||
private boolean launchSameAppAdjacently(PendingIntent pendingIntent1,
|
||||
PendingIntent pendingIntent2) {
|
||||
final String targetPackageName = getPackageName(pendingIntent1.getIntent());
|
||||
final String adjacentPackageName = getPackageName(pendingIntent2.getIntent());
|
||||
return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
|
||||
return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/** Returns {@code true} if the component supports multi-instances split. */
|
||||
boolean supportMultiInstancesSplit(@Nullable ComponentName launching) {
|
||||
if (launching == null) return false;
|
||||
|
||||
final String packageName = launching.getPackageName();
|
||||
for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
|
||||
if (mAppsSupportMultiInstances[i].equals(packageName)) {
|
||||
return true;
|
||||
boolean supportMultiInstancesSplit(String packageName) {
|
||||
if (packageName != null) {
|
||||
for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
|
||||
if (mAppsSupportMultiInstances[i].equals(packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1011,7 +999,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
InstanceId instanceId) {
|
||||
executeRemoteCallWithTaskPermission(mController,
|
||||
"startShortcutAndTaskWithLegacyTransition", (controller) ->
|
||||
controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
|
||||
controller.startShortcutAndTaskWithLegacyTransition(
|
||||
shortcutInfo, options1, taskId, options2, splitPosition,
|
||||
splitRatio, adapter, instanceId));
|
||||
}
|
||||
|
||||
@@ -36,12 +36,11 @@ import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
|
||||
|
||||
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
|
||||
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
@@ -76,8 +75,10 @@ import android.app.ActivityOptions;
|
||||
import android.app.IActivityTaskManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
@@ -87,6 +88,7 @@ import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.Choreographer;
|
||||
@@ -370,7 +372,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
int sideStagePosition;
|
||||
if (stageType == STAGE_TYPE_MAIN) {
|
||||
targetStage = mMainStage;
|
||||
sideStagePosition = SplitLayout.reversePosition(stagePosition);
|
||||
sideStagePosition = reverseSplitPosition(stagePosition);
|
||||
} else if (stageType == STAGE_TYPE_SIDE) {
|
||||
targetStage = mSideStage;
|
||||
sideStagePosition = stagePosition;
|
||||
@@ -428,6 +430,72 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
return mLogger;
|
||||
}
|
||||
|
||||
void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
|
||||
Bundle options, UserHandle user) {
|
||||
final boolean isEnteringSplit = !isSplitActive();
|
||||
|
||||
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
|
||||
@Override
|
||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
||||
RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers,
|
||||
RemoteAnimationTarget[] nonApps,
|
||||
final IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
boolean openingToSide = false;
|
||||
if (apps != null) {
|
||||
for (int i = 0; i < apps.length; ++i) {
|
||||
if (apps[i].mode == MODE_OPENING
|
||||
&& mSideStage.containsTask(apps[i].taskId)) {
|
||||
openingToSide = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnteringSplit && !openingToSide) {
|
||||
mMainExecutor.execute(() -> exitSplitScreen(
|
||||
mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
|
||||
EXIT_REASON_UNKNOWN));
|
||||
}
|
||||
|
||||
if (finishedCallback != null) {
|
||||
try {
|
||||
finishedCallback.onAnimationFinished();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error finishing legacy transition: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEnteringSplit && openingToSide) {
|
||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||
prepareEvictNonOpeningChildTasks(position, apps, evictWct);
|
||||
mSyncQueue.queue(evictWct);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onAnimationCancelled(boolean isKeyguardOccluded) {
|
||||
if (isEnteringSplit) {
|
||||
mMainExecutor.execute(() -> exitSplitScreen(
|
||||
mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
|
||||
EXIT_REASON_UNKNOWN));
|
||||
}
|
||||
}
|
||||
};
|
||||
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
|
||||
null /* wct */);
|
||||
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
|
||||
0 /* duration */, 0 /* statusBarTransitionDelay */);
|
||||
ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
|
||||
activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
|
||||
try {
|
||||
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
|
||||
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
|
||||
activityOptions.toBundle(), user);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Slog.e(TAG, "Failed to launch shortcut", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Launches an activity into split. */
|
||||
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
|
||||
@Nullable Bundle options) {
|
||||
@@ -899,7 +967,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
case STAGE_TYPE_MAIN: {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
// Set the side stage opposite of what we want to the main stage.
|
||||
setSideStagePosition(SplitLayout.reversePosition(position), wct);
|
||||
setSideStagePosition(reverseSplitPosition(position), wct);
|
||||
} else {
|
||||
position = getMainStagePosition();
|
||||
}
|
||||
@@ -923,7 +991,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
@SplitPosition
|
||||
int getMainStagePosition() {
|
||||
return SplitLayout.reversePosition(mSideStagePosition);
|
||||
return reverseSplitPosition(mSideStagePosition);
|
||||
}
|
||||
|
||||
int getTaskId(@SplitPosition int splitPosition) {
|
||||
@@ -950,7 +1018,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
|
||||
insets -> {
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), wct);
|
||||
setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
|
||||
mSyncQueue.queue(wct);
|
||||
mSyncQueue.runInSync(st -> {
|
||||
updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
|
||||
@@ -1694,12 +1762,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
}
|
||||
}
|
||||
|
||||
boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
|
||||
return taskInfo.supportsMultiWindow
|
||||
&& ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
|
||||
&& ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
|
||||
final boolean mainStageToTop =
|
||||
|
||||
@@ -27,8 +27,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -201,7 +199,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
|
||||
ActivityManager.RunningTaskInfo topRunningTask =
|
||||
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
|
||||
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
|
||||
|
||||
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||
|
||||
@@ -222,7 +219,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
|
||||
ActivityManager.RunningTaskInfo topRunningTask =
|
||||
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
|
||||
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
|
||||
// Put the same component into a task in the background
|
||||
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
|
||||
doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
|
||||
|
||||
Reference in New Issue
Block a user