Merge "Launch initial split from taskbar in overview app menu" into tm-qpr-dev

This commit is contained in:
Vinit Nayak
2023-01-28 05:25:12 +00:00
committed by Android (Google) Code Review
15 changed files with 332 additions and 188 deletions

View File

@@ -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<QuickstepLauncher> {
private final int mSplitPlaceholderSize;
private final int mSplitPlaceholderInset;
private final Rect mTempRect = new Rect();
private final SplitPositionOption mPosition;
class SplitSelectSystemShortcut extends SplitShortcut<QuickstepLauncher> {
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<Task>) 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);
}
}
}

View File

@@ -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<T>(
iconResId: Int,
labelResId: Int,
target: T,
itemInfo: ItemInfo?,
originalView: View?,
protected val position: SplitPositionOption
) : SystemShortcut<T>(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)
}
}

View File

@@ -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) {

View File

@@ -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);
}
};

View File

@@ -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<PackageUserKey> 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<SystemShortcut.Factory> getSystemShortcuts() {
// concat a Stream of split options with a Stream of APP_INFO
Stream<SystemShortcut.Factory> 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<BaseTaskbarContext> 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<BaseTaskbarContext> {
private final SplitPositionOption mPosition;
private static class TaskbarSplitShortcut extends
SplitShortcut<BaseTaskbarContext> {
/**
* 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<InstanceId, com.android.launcher3.logging.InstanceId> 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);
}

View File

@@ -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<Task>) 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.
*/

View File

@@ -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);

View File

@@ -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;
@@ -92,6 +100,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;
@@ -133,6 +142,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;
@@ -150,9 +160,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;
@@ -168,6 +180,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;
@@ -362,7 +375,7 @@ public class QuickstepLauncher extends Launcher {
Stream<SystemShortcut.Factory> 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<SplitPositionOption> positions =
recentsView.getPagedOrientationHandler().getSplitPositionOptions(
mDeviceProfile);
@@ -548,6 +561,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<Task>) 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();

View File

@@ -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<RecentsActivity, RecentsSta
}
@Override
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
super.initiateSplitSelect(splitSelectSource);
mActivity.getStateManager().goToState(OVERVIEW_SPLIT_SELECT);
}

View File

@@ -90,6 +90,8 @@ public class SplitSelectStateController {
private Intent mSecondTaskIntent;
private int mSecondTaskId = INVALID_TASK_ID;
private boolean mRecentsAnimationRunning;
/** If {@code true}, animates the existing task view split placeholder view */
private boolean mAnimateCurrentTaskDismissal;
@Nullable
private UserHandle mUser;
/** If not null, this is the TaskView we want to launch from */
@@ -111,22 +113,15 @@ public class SplitSelectStateController {
}
/**
* To be called after first task selected in Overview.
* @param alreadyRunningTask if set to {@link android.app.ActivityTaskManager#INVALID_TASK_ID}
* then @param intent will be used to launch the initial task
* @param intent will be ignored if @param alreadyRunningTask is set
*/
public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent, ItemInfo itemInfo) {
mInitialTaskId = task.key.id;
setInitialData(stagePosition, splitEvent, itemInfo);
}
/**
* To be called after first task selected from home or all apps.
*/
public void setInitialTaskSelect(Intent intent, @StagePosition int stagePosition,
public void setInitialTaskSelect(@Nullable Intent intent, @StagePosition int stagePosition,
@NonNull ItemInfo itemInfo, StatsLogManager.EventEnum splitEvent,
@Nullable Task alreadyRunningTask) {
if (alreadyRunningTask != null) {
mInitialTaskId = alreadyRunningTask.key.id;
int alreadyRunningTask) {
if (alreadyRunningTask != INVALID_TASK_ID) {
mInitialTaskId = alreadyRunningTask;
} else {
mInitialTaskIntent = intent;
mUser = itemInfo.user;
@@ -348,6 +343,14 @@ public class SplitSelectStateController {
return null;
}
public boolean isAnimateCurrentTaskDismissal() {
return mAnimateCurrentTaskDismissal;
}
public void setAnimateCurrentTaskDismissal(boolean animateCurrentTaskDismissal) {
mAnimateCurrentTaskDismissal = animateCurrentTaskDismissal;
}
/**
* Requires Shell Transitions
*/
@@ -454,6 +457,7 @@ public class SplitSelectStateController {
mLaunchingTaskView = null;
mItemInfo = null;
mSplitEvent = null;
mAnimateCurrentTaskDismissal = false;
}
/**

View File

@@ -34,13 +34,13 @@ import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.LauncherState;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.PendingSplitSelectInfo;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.util.SplitSelectStateController;
@@ -199,7 +199,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
}
@Override
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
super.initiateSplitSelect(splitSelectSource);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
}

View File

@@ -16,6 +16,7 @@
package com.android.quickstep.views;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Surface.ROTATION_0;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
@@ -144,7 +145,6 @@ import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.QuickstepSystemShortcut;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -156,6 +156,7 @@ import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
@@ -677,7 +678,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private SplitInstructionsView mSplitInstructionsView;
@Nullable
private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource;
private SplitSelectSource mSplitSelectSource;
/**
* Keeps track of the index of the TaskView that split screen was initialized with so we know
@@ -1607,8 +1608,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// If we are entering Overview as a result of initiating a split from somewhere else
// (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
Task stagedTaskToBeRemovedFromGrid =
mSplitSelectSource != null ? mSplitSelectSource.alreadyRunningTask : null;
int stagedTaskIdToBeRemovedFromGrid = mSplitSelectSource != null
? mSplitSelectSource.alreadyRunningTaskId
: INVALID_TASK_ID;
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
@@ -1617,8 +1619,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 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<ACTIVITY_TYPE extends StatefulActivity<STATE_T
addView(taskView);
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
if (groupTask.task1.equals(stagedTaskToBeRemovedFromGrid)) {
if (groupTask.task1.key.id == stagedTaskIdToBeRemovedFromGrid) {
taskView.bind(groupTask.task2, mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
@@ -3019,7 +3021,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
RectF startingTaskRect = new RectF();
safeRemoveDragLayerView(mFirstFloatingTaskView);
if (mSplitHiddenTaskView != null) {
if (mSplitSelectStateController.isAnimateCurrentTaskDismissal()) {
// Create the split select animation from Overview
mSplitHiddenTaskView.setThumbnailVisibility(INVISIBLE);
anim.setViewAlpha(mSplitHiddenTaskView.getIconView(), 0, clampToProgress(LINEAR,
@@ -4393,29 +4395,38 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
initiateSplitSelect(taskView, defaultSplitPosition, LAUNCHER_OVERVIEW_ACTIONS_SPLIT);
}
/** TODO(b/266477929): Consolidate this call w/ the one below */
public void initiateSplitSelect(TaskView taskView, @StagePosition int stagePosition,
StatsLogManager.EventEnum splitEvent) {
mSplitHiddenTaskView = taskView;
mSplitSelectStateController.setInitialTaskSelect(taskView.getTask(),
stagePosition, splitEvent, taskView.getItemInfo());
mSplitSelectStateController.setInitialTaskSelect(null /*intent*/,
stagePosition, taskView.getItemInfo(), splitEvent, taskView.mTask.key.id);
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
}
/**
* Called when staging a split from Home/AllApps, using the icon long-press menu.
* Called when staging a split from Home/AllApps/Overview (Taskbar),
* using the icon long-press menu.
* Attempts to initiate split with an existing taskView, if one exists
*/
public void initiateSplitSelect(QuickstepSystemShortcut.SplitSelectSource splitSelectSource) {
public void initiateSplitSelect(SplitSelectSource splitSelectSource) {
mSplitSelectSource = splitSelectSource;
mSplitHiddenTaskView = getTaskViewByTaskId(splitSelectSource.alreadyRunningTaskId);
mSplitHiddenTaskViewIndex = indexOfChild(mSplitHiddenTaskView);
mSplitSelectStateController
.setAnimateCurrentTaskDismissal(splitSelectSource.animateCurrentTaskDismissal);
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTask);
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
}
/**
* Modifies a PendingAnimation with the animations for entering split staging
*/
public void createSplitSelectInitAnimation(PendingAnimation builder, int duration) {
if (mSplitHiddenTaskView != null) {
if (mSplitSelectStateController.isAnimateCurrentTaskDismissal()) {
// Splitting from Overview
createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
true /* dismissingForSplitSelection*/);

View File

@@ -40,6 +40,7 @@ import java.util.List;
public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
implements View.OnClickListener {
private static final String TAG = SystemShortcut.class.getSimpleName();
private final int mIconResId;
protected final int mLabelResId;
protected int mAccessibilityActionId;

View File

@@ -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;
}
}
}

View File

@@ -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.
*/