Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +0 −13 Original line number Diff line number Diff line Loading @@ -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. */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * 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); } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +85 −97 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -480,41 +476,56 @@ 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 */, mStageCoordinator.startShortcut(packageName, shortcutId, position, activityOptions.toBundle(), user); } catch (ActivityNotFoundException e) { Slog.e(TAG, "Failed to launch shortcut", e); } 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); } /** * See {@link #startIntent(PendingIntent, Intent, int, Bundle)} * @param instanceId to be used by {@link SplitscreenEventLogger} Loading @@ -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"); Loading @@ -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"); Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -636,65 +655,34 @@ 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; } } 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; taskInfo = mRecentTasksOptional .map(recentTasks -> recentTasks.getTopRunningTask()) .orElse(null); if (!isValidToSplit(taskInfo)) { return null; } 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(); boolean supportMultiInstancesSplit(String packageName) { if (packageName != null) { for (int i = 0; i < mAppsSupportMultiInstances.length; i++) { if (mAppsSupportMultiInstances[i].equals(packageName)) { return true; } } } return false; } Loading Loading @@ -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)); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +74 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +0 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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()); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +0 −13 Original line number Diff line number Diff line Loading @@ -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. */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * 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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +85 −97 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -480,41 +476,56 @@ 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 */, mStageCoordinator.startShortcut(packageName, shortcutId, position, activityOptions.toBundle(), user); } catch (ActivityNotFoundException e) { Slog.e(TAG, "Failed to launch shortcut", e); } 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); } /** * See {@link #startIntent(PendingIntent, Intent, int, Bundle)} * @param instanceId to be used by {@link SplitscreenEventLogger} Loading @@ -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"); Loading @@ -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"); Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -636,65 +655,34 @@ 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; } } 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; taskInfo = mRecentTasksOptional .map(recentTasks -> recentTasks.getTopRunningTask()) .orElse(null); if (!isValidToSplit(taskInfo)) { return null; } 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(); boolean supportMultiInstancesSplit(String packageName) { if (packageName != null) { for (int i = 0; i < mAppsSupportMultiInstances.length; i++) { if (mAppsSupportMultiInstances[i].equals(packageName)) { return true; } } } return false; } Loading Loading @@ -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)); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +74 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +0 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading @@ -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()); Loading