Merge "Support starting a shortcut into multi-instances split" into tm-qpr-dev
This commit is contained in:
@@ -699,19 +699,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
return bounds.width() > bounds.height();
|
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.
|
* 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_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_TOP_OR_LEFT;
|
||||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
|
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_SIDE;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||||
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
|
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.ActivityOptions;
|
||||||
import android.app.ActivityTaskManager;
|
import android.app.ActivityTaskManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.LauncherApps;
|
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Bundle;
|
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.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
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.SplitScreenConstants.SplitPosition;
|
||||||
|
import com.android.wm.shell.common.split.SplitScreenUtils;
|
||||||
import com.android.wm.shell.draganddrop.DragAndDropController;
|
import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||||
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
|
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
@@ -318,10 +318,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
return mStageCoordinator;
|
return mStageCoordinator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
|
|
||||||
return mStageCoordinator.isValidToEnterSplitScreen(taskInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
|
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
|
||||||
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
|
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
|
||||||
@@ -480,39 +476,54 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
@Override
|
@Override
|
||||||
public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
|
public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
|
||||||
@Nullable Bundle options, UserHandle user) {
|
@Nullable Bundle options, UserHandle user) {
|
||||||
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
|
if (options == null) options = new Bundle();
|
||||||
@Override
|
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
|
||||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
|
||||||
RemoteAnimationTarget[] apps,
|
if (samePackage(packageName, getPackageName(reverseSplitPosition(position)))) {
|
||||||
RemoteAnimationTarget[] wallpapers,
|
if (supportMultiInstancesSplit(packageName)) {
|
||||||
RemoteAnimationTarget[] nonApps,
|
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
|
||||||
final IRemoteAnimationFinishedCallback finishedCallback) {
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||||
try {
|
} else if (isSplitScreenVisible()) {
|
||||||
finishedCallback.onAnimationFinished();
|
mStageCoordinator.switchSplitPosition("startShortcut");
|
||||||
} catch (RemoteException e) {
|
return;
|
||||||
Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
|
} else {
|
||||||
}
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
|
||||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
"Cancel entering split as not supporting multi-instances");
|
||||||
mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
|
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
|
||||||
mSyncQueue.queue(evictWct);
|
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,
|
@SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
|
||||||
InstanceId instanceId) {
|
InstanceId instanceId) {
|
||||||
Intent fillInIntent = null;
|
Intent fillInIntent = null;
|
||||||
if (launchSameAppAdjacently(pendingIntent, taskId)) {
|
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
|
||||||
if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
|
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
|
||||||
|
if (samePackage(packageName1, packageName2)) {
|
||||||
|
if (supportMultiInstancesSplit(packageName1)) {
|
||||||
fillInIntent = new Intent();
|
fillInIntent = new Intent();
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding 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,
|
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
|
||||||
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
|
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
|
||||||
Intent fillInIntent = null;
|
Intent fillInIntent = null;
|
||||||
if (launchSameAppAdjacently(pendingIntent, taskId)) {
|
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
|
||||||
if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
|
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
|
||||||
|
if (samePackage(packageName1, packageName2)) {
|
||||||
|
if (supportMultiInstancesSplit(packageName1)) {
|
||||||
fillInIntent = new Intent();
|
fillInIntent = new Intent();
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding 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) {
|
float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
|
||||||
Intent fillInIntent1 = null;
|
Intent fillInIntent1 = null;
|
||||||
Intent fillInIntent2 = null;
|
Intent fillInIntent2 = null;
|
||||||
if (launchSameAppAdjacently(pendingIntent1, pendingIntent2)) {
|
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
|
||||||
if (supportMultiInstancesSplit(pendingIntent1.getIntent().getComponent())) {
|
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
|
||||||
|
if (samePackage(packageName1, packageName2)) {
|
||||||
|
if (supportMultiInstancesSplit(packageName1)) {
|
||||||
fillInIntent1 = new Intent();
|
fillInIntent1 = new Intent();
|
||||||
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
fillInIntent2 = new Intent();
|
fillInIntent2 = new Intent();
|
||||||
@@ -602,13 +619,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
if (fillInIntent == null) fillInIntent = new Intent();
|
if (fillInIntent == null) fillInIntent = new Intent();
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
||||||
|
|
||||||
if (launchSameAppAdjacently(position, intent)) {
|
final String packageName1 = SplitScreenUtils.getPackageName(intent);
|
||||||
final ComponentName launching = intent.getIntent().getComponent();
|
final String packageName2 = getPackageName(reverseSplitPosition(position));
|
||||||
if (supportMultiInstancesSplit(launching)) {
|
if (SplitScreenUtils.samePackage(packageName1, packageName2)) {
|
||||||
|
if (supportMultiInstancesSplit(packageName1)) {
|
||||||
// To prevent accumulating large number of instances in the background, reuse task
|
// To prevent accumulating large number of instances in the background, reuse task
|
||||||
// in the background with priority.
|
// in the background with priority.
|
||||||
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
|
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
|
||||||
.map(recentTasks -> recentTasks.findTaskInBackground(launching))
|
.map(recentTasks -> recentTasks.findTaskInBackground(
|
||||||
|
intent.getIntent().getComponent()))
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
if (taskInfo != null) {
|
if (taskInfo != null) {
|
||||||
startTask(taskInfo.taskId, position, options);
|
startTask(taskInfo.taskId, position, options);
|
||||||
@@ -636,63 +655,32 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
|
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
|
@Nullable
|
||||||
private String getPackageName(Intent intent) {
|
private String getPackageName(@SplitPosition int position) {
|
||||||
if (intent == null || intent.getComponent() == null) {
|
ActivityManager.RunningTaskInfo taskInfo;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return intent.getComponent().getPackageName();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean launchSameAppAdjacently(@SplitPosition int position,
|
|
||||||
PendingIntent pendingIntent) {
|
|
||||||
ActivityManager.RunningTaskInfo adjacentTaskInfo = null;
|
|
||||||
if (isSplitScreenVisible()) {
|
if (isSplitScreenVisible()) {
|
||||||
adjacentTaskInfo = getTaskInfo(SplitLayout.reversePosition(position));
|
taskInfo = getTaskInfo(position);
|
||||||
} else {
|
} else {
|
||||||
adjacentTaskInfo = mRecentTasksOptional
|
taskInfo = mRecentTasksOptional
|
||||||
.map(recentTasks -> recentTasks.getTopRunningTask()).orElse(null);
|
.map(recentTasks -> recentTasks.getTopRunningTask())
|
||||||
if (!isValidToEnterSplitScreen(adjacentTaskInfo)) {
|
.orElse(null);
|
||||||
return false;
|
if (!isValidToSplit(taskInfo)) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adjacentTaskInfo == null) {
|
return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
/** Returns {@code true} if the component supports multi-instances split. */
|
boolean supportMultiInstancesSplit(String packageName) {
|
||||||
boolean supportMultiInstancesSplit(@Nullable ComponentName launching) {
|
if (packageName != null) {
|
||||||
if (launching == null) return false;
|
for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
|
||||||
|
if (mAppsSupportMultiInstances[i].equals(packageName)) {
|
||||||
final String packageName = launching.getPackageName();
|
return true;
|
||||||
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) {
|
InstanceId instanceId) {
|
||||||
executeRemoteCallWithTaskPermission(mController,
|
executeRemoteCallWithTaskPermission(mController,
|
||||||
"startShortcutAndTaskWithLegacyTransition", (controller) ->
|
"startShortcutAndTaskWithLegacyTransition", (controller) ->
|
||||||
controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
|
controller.startShortcutAndTaskWithLegacyTransition(
|
||||||
shortcutInfo, options1, taskId, options2, splitPosition,
|
shortcutInfo, options1, taskId, options2, splitPosition,
|
||||||
splitRatio, adapter, instanceId));
|
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 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.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.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_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_TOP_OR_LEFT;
|
||||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
|
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_MAIN;
|
||||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
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.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||||
@@ -76,8 +75,10 @@ import android.app.ActivityOptions;
|
|||||||
import android.app.IActivityTaskManager;
|
import android.app.IActivityTaskManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.WindowConfiguration;
|
import android.app.WindowConfiguration;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.LauncherApps;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -87,6 +88,7 @@ import android.os.Debug;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
|
import android.os.UserHandle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
@@ -370,7 +372,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
int sideStagePosition;
|
int sideStagePosition;
|
||||||
if (stageType == STAGE_TYPE_MAIN) {
|
if (stageType == STAGE_TYPE_MAIN) {
|
||||||
targetStage = mMainStage;
|
targetStage = mMainStage;
|
||||||
sideStagePosition = SplitLayout.reversePosition(stagePosition);
|
sideStagePosition = reverseSplitPosition(stagePosition);
|
||||||
} else if (stageType == STAGE_TYPE_SIDE) {
|
} else if (stageType == STAGE_TYPE_SIDE) {
|
||||||
targetStage = mSideStage;
|
targetStage = mSideStage;
|
||||||
sideStagePosition = stagePosition;
|
sideStagePosition = stagePosition;
|
||||||
@@ -428,6 +430,72 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
return mLogger;
|
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. */
|
/** Launches an activity into split. */
|
||||||
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
|
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
|
||||||
@Nullable Bundle options) {
|
@Nullable Bundle options) {
|
||||||
@@ -899,7 +967,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
case STAGE_TYPE_MAIN: {
|
case STAGE_TYPE_MAIN: {
|
||||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||||
// Set the side stage opposite of what we want to the main stage.
|
// Set the side stage opposite of what we want to the main stage.
|
||||||
setSideStagePosition(SplitLayout.reversePosition(position), wct);
|
setSideStagePosition(reverseSplitPosition(position), wct);
|
||||||
} else {
|
} else {
|
||||||
position = getMainStagePosition();
|
position = getMainStagePosition();
|
||||||
}
|
}
|
||||||
@@ -923,7 +991,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
@SplitPosition
|
@SplitPosition
|
||||||
int getMainStagePosition() {
|
int getMainStagePosition() {
|
||||||
return SplitLayout.reversePosition(mSideStagePosition);
|
return reverseSplitPosition(mSideStagePosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getTaskId(@SplitPosition int splitPosition) {
|
int getTaskId(@SplitPosition int splitPosition) {
|
||||||
@@ -950,7 +1018,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
|
mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
|
||||||
insets -> {
|
insets -> {
|
||||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), wct);
|
setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
|
||||||
mSyncQueue.queue(wct);
|
mSyncQueue.queue(wct);
|
||||||
mSyncQueue.runInSync(st -> {
|
mSyncQueue.runInSync(st -> {
|
||||||
updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
|
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
|
@Override
|
||||||
public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
|
public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
|
||||||
final boolean mainStageToTop =
|
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 com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
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.junit.Assume.assumeTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
@@ -201,7 +199,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
|
|||||||
ActivityManager.RunningTaskInfo topRunningTask =
|
ActivityManager.RunningTaskInfo topRunningTask =
|
||||||
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
|
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
|
||||||
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
|
|
||||||
|
|
||||||
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||||
|
|
||||||
@@ -222,7 +219,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
|
|||||||
ActivityManager.RunningTaskInfo topRunningTask =
|
ActivityManager.RunningTaskInfo topRunningTask =
|
||||||
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
|
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
|
||||||
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
|
|
||||||
// Put the same component into a task in the background
|
// Put the same component into a task in the background
|
||||||
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
|
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
|
||||||
doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
|
doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
|
||||||
|
|||||||
Reference in New Issue
Block a user