From 49dc6d27122888709d4041888f7b30bfc964f637 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 12 Jan 2023 18:50:36 -0800 Subject: [PATCH] Launch initial split from taskbar in overview app menu * Consolidated init calls in SplitSelectStateController * Also add support to launch from taskbar all apps * Add logic in SplitSelectStateController to know whether or not we need to dismiss existing TaskView vs relying on mSplitHiddenTaskView null check * Default click handling for SplitShortcut is to start split selection mode Bug: 251747761 Test: Initiated split from smart actions, thumbnail app icon, home, taskbar in overview, all apps. Saw it choose the latest thumbnail Change-Id: Ib4f64e619c97615af458a19a9c0efd86c92979d9 --- .../popup/QuickstepSystemShortcut.java | 138 +----------------- .../launcher3/splitscreen/SplitShortcut.kt | 93 ++++++++++++ .../taskbar/TaskbarActivityContext.java | 7 + .../TaskbarLauncherStateController.java | 3 +- .../taskbar/TaskbarPopupController.java | 48 +++--- .../taskbar/TaskbarUIController.java | 20 +++ .../overlay/TaskbarOverlayContext.java | 16 ++ .../uioverrides/QuickstepLauncher.java | 73 ++++++++- .../fallback/FallbackRecentsView.java | 4 +- .../util/SplitSelectStateController.java | 32 ++-- .../quickstep/views/LauncherRecentsView.java | 4 +- .../android/quickstep/views/RecentsView.java | 39 +++-- .../launcher3/popup/SystemShortcut.java | 1 + .../util/SplitConfigurationOptions.java | 35 +++++ .../launcher3/views/ActivityContext.java | 7 + 15 files changed, 332 insertions(+), 188 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt diff --git a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java index 45d1b11c56..184ea717cd 100644 --- a/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java +++ b/quickstep/src/com/android/launcher3/popup/QuickstepSystemShortcut.java @@ -15,38 +15,14 @@ */ package com.android.launcher3.popup; -import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; -import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition; -import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.util.Log; import android.view.View; -import androidx.annotation.Nullable; - -import com.android.launcher3.AbstractFloatingView; -import com.android.launcher3.R; -import com.android.launcher3.anim.PendingAnimation; -import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.splitscreen.SplitShortcut; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; -import com.android.quickstep.util.SplitSelectStateController; -import com.android.quickstep.views.FloatingTaskView; -import com.android.quickstep.views.RecentsView; -import com.android.systemui.shared.recents.model.Task; - -import java.util.function.Consumer; +/** {@link SystemShortcut.Factory} implementation to create workspace split shortcuts */ public interface QuickstepSystemShortcut { String TAG = QuickstepSystemShortcut.class.getSimpleName(); @@ -58,116 +34,12 @@ public interface QuickstepSystemShortcut { originalView, position); } - class SplitSelectSystemShortcut extends SystemShortcut { - - private final int mSplitPlaceholderSize; - private final int mSplitPlaceholderInset; - - private final Rect mTempRect = new Rect(); - private final SplitPositionOption mPosition; + class SplitSelectSystemShortcut extends SplitShortcut { public SplitSelectSystemShortcut(QuickstepLauncher launcher, ItemInfo itemInfo, View originalView, SplitPositionOption position) { - super(position.iconResId, position.textResId, launcher, itemInfo, originalView); - - mPosition = position; - - mSplitPlaceholderSize = launcher.getResources().getDimensionPixelSize( - R.dimen.split_placeholder_size); - mSplitPlaceholderInset = launcher.getResources().getDimensionPixelSize( - R.dimen.split_placeholder_inset); - } - - @Override - public void onClick(View view) { - // Initiate splitscreen from the Home screen or Home All Apps - Bitmap bitmap; - Intent intent; - if (mItemInfo instanceof WorkspaceItemInfo) { - final WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo; - bitmap = workspaceItemInfo.bitmap.icon; - intent = workspaceItemInfo.intent; - } else if (mItemInfo instanceof com.android.launcher3.model.data.AppInfo) { - final com.android.launcher3.model.data.AppInfo appInfo = - (com.android.launcher3.model.data.AppInfo) mItemInfo; - bitmap = appInfo.bitmap.icon; - intent = appInfo.intent; - } else { - Log.e(TAG, "unknown item type"); - return; - } - - StatsLogManager.EventEnum splitEvent = getLogEventForPosition(mPosition.stagePosition); - RecentsView recentsView = mTarget.getOverviewPanel(); - // Check if there is already an instance of this app running, if so, initiate the split - // using that. - recentsView.findLastActiveTaskAndRunCallback( - intent.getComponent(), - (Consumer) foundTask -> { - SplitSelectSource source = new SplitSelectSource(mOriginalView, - new BitmapDrawable(bitmap), intent, mPosition, mItemInfo, - splitEvent, foundTask); - if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { - startSplitToHome(source); - } else { - recentsView.initiateSplitSelect(source); - } - } - ); - } - - private void startSplitToHome(SplitSelectSource source) { - AbstractFloatingView.closeAllOpenViews(mTarget); - - SplitSelectStateController controller = mTarget.getSplitSelectStateController(); - controller.setInitialTaskSelect(source.intent, source.position.stagePosition, - source.itemInfo, source.splitEvent, source.alreadyRunningTask); - - RecentsView recentsView = mTarget.getOverviewPanel(); - recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds( - mSplitPlaceholderSize, mSplitPlaceholderInset, mTarget.getDeviceProfile(), - controller.getActiveSplitStagePosition(), mTempRect); - - PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration()); - RectF startingTaskRect = new RectF(); - final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(mTarget, - source.view, null /* thumbnail */, source.drawable, startingTaskRect); - floatingTaskView.setAlpha(1); - floatingTaskView.addStagingAnimation(anim, startingTaskRect, mTempRect, - false /* fadeWithThumbnail */, true /* isStagedTask */); - controller.setFirstFloatingTaskView(floatingTaskView); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - mTarget.getDragLayer().removeView(floatingTaskView); - controller.resetState(); - } - }); - anim.buildAnim().start(); - } - } - - class SplitSelectSource { - - public final View view; - public final Drawable drawable; - public final Intent intent; - public final SplitPositionOption position; - public final ItemInfo itemInfo; - public final StatsLogManager.EventEnum splitEvent; - @Nullable - public final Task alreadyRunningTask; - - public SplitSelectSource(View view, Drawable drawable, Intent intent, - SplitPositionOption position, ItemInfo itemInfo, - StatsLogManager.EventEnum splitEvent, @Nullable Task foundTask) { - this.view = view; - this.drawable = drawable; - this.intent = intent; - this.position = position; - this.itemInfo = itemInfo; - this.splitEvent = splitEvent; - this.alreadyRunningTask = foundTask; + super(position.iconResId, position.textResId, launcher, itemInfo, originalView, + position); } } } diff --git a/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt b/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt new file mode 100644 index 0000000000..20c8c44142 --- /dev/null +++ b/quickstep/src/com/android/launcher3/splitscreen/SplitShortcut.kt @@ -0,0 +1,93 @@ +/* + * 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.launcher3.splitscreen + +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.util.Log +import android.view.View +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.model.data.WorkspaceItemInfo +import com.android.launcher3.popup.QuickstepSystemShortcut +import com.android.launcher3.popup.SystemShortcut +import com.android.launcher3.util.SplitConfigurationOptions +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption +import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource +import com.android.launcher3.views.ActivityContext + +/** + * Shortcut to allow starting split. Default interaction for [onClick] is to launch + * split selection mode + */ +abstract class SplitShortcut( + iconResId: Int, + labelResId: Int, + target: T, + itemInfo: ItemInfo?, + originalView: View?, + protected val position: SplitPositionOption +) : SystemShortcut(iconResId, labelResId, target, itemInfo, originalView) where +T : Context?, +T : ActivityContext? { + private val TAG = SystemShortcut::class.java.simpleName + + // Initiate splitscreen from the Home screen or Home All Apps + protected val splitSelectSource: SplitSelectSource? + get() { + // Initiate splitscreen from the Home screen or Home All Apps + val bitmap: Bitmap + val intent: Intent + when (mItemInfo) { + is WorkspaceItemInfo -> { + val workspaceItemInfo = mItemInfo + bitmap = workspaceItemInfo.bitmap.icon + intent = workspaceItemInfo.intent + } + is com.android.launcher3.model.data.AppInfo -> { + val appInfo = mItemInfo + bitmap = appInfo.bitmap.icon + intent = appInfo.intent + } + else -> { + Log.e(TAG, "unknown item type") + return null + } + } + val splitEvent = + SplitConfigurationOptions.getLogEventForPosition(position.stagePosition) + return SplitSelectSource( + mOriginalView, + BitmapDrawable(bitmap), + intent, + position, + mItemInfo, + splitEvent + ) + } + + /** Starts split selection on the provided [mTarget] */ + override fun onClick(view: View?) { + val splitSelectSource = splitSelectSource + if (splitSelectSource == null) { + Log.w(QuickstepSystemShortcut.TAG, "no split selection source") + return + } + mTarget!!.startSplitSelection(splitSelectSource) + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 8a5b2c5e17..accbf214bb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static android.os.Trace.TRACE_TAG_APP; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; @@ -92,6 +93,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.NavigationMode; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.SettingsCache; +import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.ViewCache; import com.android.launcher3.views.ActivityContext; @@ -754,6 +756,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } } + @Override + public void startSplitSelection(SplitSelectSource splitSelectSource) { + mControllers.uiController.startSplitSelection(splitSelectSource); + } + protected void onTaskbarIconClicked(View view) { Object tag = view.getTag(); if (tag instanceof Task) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index d9773d4081..62f12936dd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -125,7 +125,8 @@ import java.util.StringJoiner; mControllers.taskbarDragController.setDisallowLongClick(disallowLongClick); mControllers.taskbarAllAppsController.setDisallowGlobalDrag(disallowGlobalDrag); mControllers.taskbarAllAppsController.setDisallowLongClick(disallowLongClick); - mControllers.taskbarPopupController.setHideSplitOptions(disallowGlobalDrag); + mControllers.taskbarPopupController.setAllowInitialSplitSelection( + disallowGlobalDrag); } }; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java index 9b27c9d026..30d6eb4dda 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -17,11 +17,9 @@ package com.android.launcher3.taskbar; import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition; -import android.content.ClipDescription; import android.content.Intent; import android.content.pm.LauncherApps; import android.graphics.Point; -import android.os.Bundle; import android.util.Pair; import android.view.MotionEvent; import android.view.View; @@ -46,6 +44,7 @@ import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.popup.PopupLiveUpdateHandler; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.shortcuts.DeepShortcutView; +import com.android.launcher3.splitscreen.SplitShortcut; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.LauncherBindableItemsContainer; import com.android.launcher3.util.PackageUserKey; @@ -75,7 +74,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba // Initialized in init. private TaskbarControllers mControllers; - private boolean mHideSplitOptions; + private boolean mAllowInitialSplitSelection; public TaskbarPopupController(TaskbarActivityContext context) { mContext = context; @@ -101,8 +100,8 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy); } - public void setHideSplitOptions(boolean hideSplitOptions) { - mHideSplitOptions = hideSplitOptions; + public void setAllowInitialSplitSelection(boolean allowInitialSplitSelection) { + mAllowInitialSplitSelection = allowInitialSplitSelection; } private void updateNotificationDots(Predicate updatedDots) { @@ -188,13 +187,9 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba } // Create a Stream of all applicable system shortcuts - // TODO(b/227800345): Add "Split bottom" option when tablet is in portrait mode. private Stream getSystemShortcuts() { // concat a Stream of split options with a Stream of APP_INFO Stream appInfo = Stream.of(APP_INFO); - if (mHideSplitOptions) { - return appInfo; - } return Stream.concat( Utilities.getSplitPositionOptions(mContext.getDeviceProfile()) @@ -261,7 +256,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba private SystemShortcut.Factory createSplitShortcutFactory( SplitPositionOption position) { return (context, itemInfo, originalView) -> new TaskbarSplitShortcut(context, itemInfo, - originalView, position); + originalView, position, mAllowInitialSplitSelection); } /** @@ -269,32 +264,43 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba * from the taskbar, as if the user performed a drag and drop split. * Includes an onClick method that initiates the actual split. */ - private static class TaskbarSplitShortcut extends SystemShortcut { - private final SplitPositionOption mPosition; + private static class TaskbarSplitShortcut extends + SplitShortcut { + /** + * If {@code true}, clicking this shortcut will not attempt to start a split app directly, + * but be the first app in split selection mode + */ + private final boolean mAllowInitialSplitSelection; - TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo, View originalView, - SplitPositionOption position) { - super(position.iconResId, position.textResId, context, itemInfo, originalView); - mPosition = position; - } + TaskbarSplitShortcut(BaseTaskbarContext context, ItemInfo itemInfo, View originalView, + SplitPositionOption position, boolean allowInitialSplitSelection) { + super(position.iconResId, position.textResId, context, itemInfo, originalView, + position); + mAllowInitialSplitSelection = allowInitialSplitSelection; + } @Override public void onClick(View view) { + AbstractFloatingView.closeAllOpenViews(mTarget); + if (mAllowInitialSplitSelection) { + super.onClick(view); + return; + } + // Initiate splitscreen from the in-app Taskbar or Taskbar All Apps Pair instanceIds = LogUtils.getShellShareableInstanceId(); mTarget.getStatsLogManager().logger() .withItemInfo(mItemInfo) .withInstanceId(instanceIds.second) - .log(getLogEventForPosition(mPosition.stagePosition)); + .log(getLogEventForPosition(getPosition().stagePosition)); - AbstractFloatingView.closeAllOpenViews(mTarget); if (mItemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) { WorkspaceItemInfo workspaceItemInfo = (WorkspaceItemInfo) mItemInfo; SystemUiProxy.INSTANCE.get(mTarget).startShortcut( workspaceItemInfo.getIntent().getPackage(), workspaceItemInfo.getDeepShortcutId(), - mPosition.stagePosition, + getPosition().stagePosition, null, workspaceItemInfo.user, instanceIds.first); @@ -305,7 +311,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba null, mItemInfo.user), new Intent(), - mPosition.stagePosition, + getPosition().stagePosition, null, instanceIds.first); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index ebb37a88fc..1c11bf7d3d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.taskbar; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + import android.content.Intent; import android.graphics.drawable.BitmapDrawable; import android.view.MotionEvent; @@ -26,6 +28,7 @@ import androidx.annotation.Nullable; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.util.DisplayController; +import com.android.launcher3.util.SplitConfigurationOptions; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; @@ -165,6 +168,23 @@ public class TaskbarUIController { return null; } + public void startSplitSelection(SplitConfigurationOptions.SplitSelectSource splitSelectSource) { + RecentsView recentsView = getRecentsView(); + if (recentsView == null) { + return; + } + recentsView.findLastActiveTaskAndRunCallback( + splitSelectSource.intent.getComponent(), + (Consumer) foundTask -> { + splitSelectSource.alreadyRunningTaskId = foundTask == null + ? INVALID_TASK_ID + : foundTask.key.id; + splitSelectSource.animateCurrentTaskDismissal = foundTask != null; + recentsView.initiateSplitSelect(splitSelectSource); + } + ); + } + /** * Uses the clicked Taskbar icon to launch a second app for splitscreen. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java index d7a0b0343f..3edb3756b4 100644 --- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.taskbar.overlay; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + import android.content.Context; import android.view.View; @@ -28,7 +30,13 @@ import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarControllers; import com.android.launcher3.taskbar.TaskbarDragController; import com.android.launcher3.taskbar.TaskbarStashController; +import com.android.launcher3.taskbar.TaskbarUIController; import com.android.launcher3.taskbar.allapps.TaskbarAllAppsContainerView; +import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; +import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.recents.model.Task; + +import java.util.function.Consumer; /** * Window context for the taskbar overlays such as All Apps and EDU. @@ -46,6 +54,7 @@ public class TaskbarOverlayContext extends BaseTaskbarContext { // We automatically stash taskbar when All Apps is opened in gesture navigation mode. private final boolean mWillTaskbarBeVisuallyStashed; private final int mStashedTaskbarHeight; + private final TaskbarUIController mUiController; public TaskbarOverlayContext( Context windowContext, @@ -61,6 +70,8 @@ public class TaskbarOverlayContext extends BaseTaskbarContext { TaskbarStashController taskbarStashController = controllers.taskbarStashController; mWillTaskbarBeVisuallyStashed = taskbarStashController.supportsVisualStashing(); mStashedTaskbarHeight = taskbarStashController.getStashedHeight(); + + mUiController = controllers.uiController; } boolean willTaskbarBeVisuallyStashed() { @@ -110,6 +121,11 @@ public class TaskbarOverlayContext extends BaseTaskbarContext { return mTaskbarContext.getPopupDataProvider(); } + @Override + public void startSplitSelection(SplitSelectSource splitSelectSource) { + mUiController.startSplitSelection(splitSelectSource); + } + @Override public DotInfo getDotInfoForItem(ItemInfo info) { return mTaskbarContext.getDotInfoForItem(info); diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 078865fd02..3606018ffd 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.os.Trace.TRACE_TAG_APP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED; @@ -32,6 +33,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.anim.Interpolators.EMPHASIZED; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE; +import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE; import static com.android.launcher3.config.FeatureFlags.ENABLE_WIDGET_PICKER_DEPTH; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; @@ -46,8 +48,11 @@ import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STA import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.app.ActivityManager; @@ -57,6 +62,8 @@ import android.content.Intent; import android.content.IntentSender; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.graphics.Rect; +import android.graphics.RectF; import android.hardware.SensorManager; import android.hardware.devicestate.DeviceStateManager; import android.media.permission.SafeCloseable; @@ -80,6 +87,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.app.viewcapture.ViewCapture; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings.Favorites; @@ -91,6 +99,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragOptions; @@ -132,6 +141,7 @@ import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.util.PendingSplitSelectInfo; import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; +import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; import com.android.launcher3.util.TouchController; import com.android.launcher3.widget.LauncherWidgetHolder; import com.android.quickstep.OverviewCommandHelper; @@ -149,9 +159,11 @@ import com.android.quickstep.util.SplitToWorkspaceController; import com.android.quickstep.util.SplitWithKeyboardShortcutController; import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.views.DesktopTaskView; +import com.android.quickstep.views.FloatingTaskView; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.unfold.UnfoldSharedComponent; import com.android.systemui.unfold.UnfoldTransitionFactory; @@ -167,6 +179,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -361,7 +374,7 @@ public class QuickstepLauncher extends Launcher { Stream base = Stream.of(WellbeingModel.SHORTCUT_FACTORY); if (ENABLE_SPLIT_FROM_WORKSPACE.get() && mDeviceProfile.isTablet) { RecentsView recentsView = getOverviewPanel(); - // TODO: Pull it out of PagedOrentationHandler for split from workspace. + // TODO(b/266482558): Pull it out of PagedOrentationHandler for split from workspace. List positions = recentsView.getPagedOrientationHandler().getSplitPositionOptions( mDeviceProfile); @@ -547,6 +560,64 @@ public class QuickstepLauncher extends Launcher { getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE); } + @Override + public void startSplitSelection(SplitSelectSource splitSelectSource) { + RecentsView recentsView = getOverviewPanel(); + // Check if there is already an instance of this app running, if so, initiate the split + // using that. + recentsView.findLastActiveTaskAndRunCallback( + splitSelectSource.intent.getComponent(), + (Consumer) foundTask -> { + splitSelectSource.alreadyRunningTaskId = foundTask == null + ? INVALID_TASK_ID + : foundTask.key.id; + if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) { + startSplitToHome(splitSelectSource); + } else { + recentsView.initiateSplitSelect(splitSelectSource); + } + } + ); + } + + /** TODO(b/266482558) Migrate into SplitSelectStateController or someplace split specific. */ + private void startSplitToHome( + SplitSelectSource source) { + AbstractFloatingView.closeAllOpenViews(this); + int splitPlaceholderSize = getResources().getDimensionPixelSize( + R.dimen.split_placeholder_size); + int splitPlaceholderInset = getResources().getDimensionPixelSize( + R.dimen.split_placeholder_inset); + Rect tempRect = new Rect(); + + SplitSelectStateController controller = getSplitSelectStateController(); + controller.setInitialTaskSelect(source.intent, source.position.stagePosition, + source.itemInfo, source.splitEvent, source.alreadyRunningTaskId); + + RecentsView recentsView = getOverviewPanel(); + recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds( + splitPlaceholderSize, splitPlaceholderInset, getDeviceProfile(), + controller.getActiveSplitStagePosition(), tempRect); + + PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration()); + RectF startingTaskRect = new RectF(); + final FloatingTaskView floatingTaskView = FloatingTaskView.getFloatingTaskView(this, + source.view, null /* thumbnail */, source.drawable, startingTaskRect); + floatingTaskView.setAlpha(1); + floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect, + false /* fadeWithThumbnail */, true /* isStagedTask */); + controller.setFirstFloatingTaskView(floatingTaskView); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + getDragLayer().removeView(floatingTaskView); + controller.resetState(); + } + }); + anim.buildAnim().start(); + } + + @Override protected void onResume() { super.onResume(); diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index bcaae99e2e..db6d56b41b 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -34,9 +34,9 @@ import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.logging.StatsLogManager; -import com.android.launcher3.popup.QuickstepSystemShortcut; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.util.SplitConfigurationOptions; +import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource; import com.android.quickstep.FallbackActivityInterface; import com.android.quickstep.GestureState; import com.android.quickstep.RecentsActivity; @@ -269,7 +269,7 @@ public class FallbackRecentsView extends RecentsView= 0; i--) { GroupTask groupTask = taskGroups.get(i); - boolean isRemovalNeeded = stagedTaskToBeRemovedFromGrid != null - && groupTask.containsTask(stagedTaskToBeRemovedFromGrid.key.id); + boolean isRemovalNeeded = stagedTaskIdToBeRemovedFromGrid != INVALID_TASK_ID + && groupTask.containsTask(stagedTaskIdToBeRemovedFromGrid); TaskView taskView; if (isRemovalNeeded && groupTask.hasMultipleTasks()) { @@ -1632,7 +1634,7 @@ public abstract class RecentsView extends ItemInfo implements View.OnClickListener { + private static final String TAG = SystemShortcut.class.getSimpleName(); private final int mIconResId; protected final int mLabelResId; protected int mAccessibilityActionId; diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java index 19a39483e1..8c5e78295b 100644 --- a/src/com/android/launcher3/util/SplitConfigurationOptions.java +++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java @@ -21,11 +21,15 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static java.lang.annotation.RetentionPolicy.SOURCE; +import android.content.Intent; import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.view.View; import androidx.annotation.IntDef; import com.android.launcher3.logging.StatsLogManager; +import com.android.launcher3.model.data.ItemInfo; import java.lang.annotation.Retention; @@ -190,4 +194,35 @@ public final class SplitConfigurationOptions { return position == STAGE_POSITION_TOP_OR_LEFT ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT; } + + public static class SplitSelectSource { + + /** Keep in sync w/ ActivityTaskManager#INVALID_TASK_ID (unreference-able) */ + private static final int INVALID_TASK_ID = -1; + + public final View view; + public final Drawable drawable; + public final Intent intent; + public final SplitPositionOption position; + public final ItemInfo itemInfo; + public final StatsLogManager.EventEnum splitEvent; + /** Represents the taskId of the first app to start in split screen */ + public int alreadyRunningTaskId = INVALID_TASK_ID; + /** + * If {@code true}, animates the view represented by {@link #alreadyRunningTaskId} into the + * split placeholder view + */ + public boolean animateCurrentTaskDismissal; + + public SplitSelectSource(View view, Drawable drawable, Intent intent, + SplitPositionOption position, ItemInfo itemInfo, + StatsLogManager.EventEnum splitEvent) { + this.view = view; + this.drawable = drawable; + this.intent = intent; + this.position = position; + this.itemInfo = itemInfo; + this.splitEvent = splitEvent; + } + } } diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 79b4cb4da4..d794f36096 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -70,6 +70,7 @@ import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; +import com.android.launcher3.util.SplitConfigurationOptions; import com.android.launcher3.util.ViewCache; import java.util.List; @@ -130,6 +131,12 @@ public interface ActivityContext { return null; } + /** Called when the first app in split screen has been selected */ + default void startSplitSelection( + SplitConfigurationOptions.SplitSelectSource splitSelectSource) { + // Overridden, intentionally empty + } + /** * The root view to support drag-and-drop and popup support. */