diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml index cd64a94bab..55400a7b24 100644 --- a/quickstep/res/layout/fallback_recents_activity.xml +++ b/quickstep/res/layout/fallback_recents_activity.xml @@ -19,6 +19,14 @@ android:layout_height="match_parent" android:fitsSystemWindows="true"> + + + + 90dp 54dp 42dp + 110dp 16dp 70dp diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 9a4487c70d..0764bb3ca3 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -22,8 +22,6 @@ Quickstep - - Split screen Pin diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index cdfd1a275c..20a645e996 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -58,8 +58,10 @@ import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskUtils; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteFadeOutAnimationListener; +import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.SplitPlaceholderView; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -215,7 +217,12 @@ public abstract class BaseQuickstepLauncher extends Launcher SysUINavigationMode.INSTANCE.get(this).updateMode(); mActionsView = findViewById(R.id.overview_actions_view); - ((RecentsView) getOverviewPanel()).init(mActionsView); + SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder); + RecentsView overviewPanel = (RecentsView) getOverviewPanel(); + splitPlaceholderView.init( + new SplitSelectStateController(SystemUiProxy.INSTANCE.get(this)) + ); + overviewPanel.init(mActionsView, splitPlaceholderView); mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this)); addTaskbarIfNecessary(); diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 0f13ef9c20..bedaefa5d6 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -32,6 +32,7 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW; import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET; import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS; import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY; +import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_TRANSLATION; import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION; import android.util.FloatProperty; @@ -44,6 +45,7 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.states.StateAnimationConfig; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.quickstep.views.RecentsView; /** @@ -105,7 +107,12 @@ public abstract class BaseRecentsViewStateController config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR)); setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR)); - setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f, + PagedOrientationHandler orientationHandler = + ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler(); + FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset( + TASK_PRIMARY_TRANSLATION, TASK_SECONDARY_TRANSLATION, mLauncher.getDeviceProfile()); + setter.setFloat(mRecentsView, taskViewsFloat, + toState.getOverviewSecondaryTranslation(mLauncher), config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR)); setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0, diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index c9de662784..750f673625 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -17,11 +17,14 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON; import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS; +import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; +import static com.android.launcher3.LauncherState.SPLIT_PLACHOLDER_VIEW; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE; import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA; import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS; import static com.android.quickstep.views.RecentsView.TASK_MODALNESS; +import static com.android.quickstep.views.SplitPlaceholderView.ALPHA_FLOAT; import android.annotation.TargetApi; import android.os.Build; @@ -77,11 +80,26 @@ public final class RecentsViewStateController extends AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals)); } + // Create or dismiss split screen select animations + LauncherState currentState = mLauncher.getStateManager().getState(); + if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) { + builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim()); + } else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) { + builder.add(mRecentsView.cancelSplitSelect(true).buildAnim()); + } + setAlphas(builder, config, toState); builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS, toState.getOverviewFullscreenProgress(), LINEAR); } + /** + * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT} + */ + private boolean isSplitSelectionState(@NonNull LauncherState toState) { + return toState == OVERVIEW_SPLIT_SELECT; + } + private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config, LauncherState state) { float clearAllButtonAlpha = (state.getVisibleElements(mLauncher) & CLEAR_ALL_BUTTON) != 0 @@ -93,6 +111,11 @@ public final class RecentsViewStateController extends propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(), MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator( ANIM_OVERVIEW_ACTIONS_FADE, LINEAR)); + + float splitPlaceholderAlpha = state.areElementsVisible(mLauncher, SPLIT_PLACHOLDER_VIEW) ? + 1 : 0; + propertySetter.setFloat(mRecentsView.getSplitPlaceholder(), ALPHA_FLOAT, + splitPlaceholderAlpha, LINEAR); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 1f68a0403b..372784a2c0 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -175,4 +175,12 @@ public class OverviewState extends LauncherState { public static OverviewState newModalTaskState(int id) { return new OverviewModalTaskState(id); } + + /** + * New Overview substate representing state where 1 app for split screen has been selected and + * pinned and user is selecting the second one + */ + public static OverviewState newSplitSelectState(int id) { + return new SplitScreenSelectState(id); + } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java new file mode 100644 index 0000000000..722d74a55a --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 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.uioverrides.states; + +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.quickstep.views.RecentsView; + +/** + * New Overview substate representing state where 1 app for split screen has been selected and + * pinned and user is selecting the second one + */ +public class SplitScreenSelectState extends OverviewState { + public SplitScreenSelectState(int id) { + super(id); + } + + @Override + public void onBackPressed(Launcher launcher) { + launcher.getStateManager().goToState(OVERVIEW); + } + + @Override + public int getVisibleElements(Launcher launcher) { + return SPLIT_PLACHOLDER_VIEW; + } + + @Override + public float getOverviewSecondaryTranslation(Launcher launcher) { + RecentsView recentsView = launcher.getOverviewPanel(); + PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler(); + int splitPosition = recentsView.getSplitPlaceholder().getSplitController() + .getActiveSplitPositionOption().mStagePosition; + int direction = orientationHandler.getSplitTranslationDirectionFactor(splitPosition); + return launcher.getResources().getDimension(R.dimen.split_placeholder_size) * direction; + } +} diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index 3d68d64e53..d3ed791b40 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -63,7 +63,9 @@ import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.fallback.RecentsDragLayer; import com.android.quickstep.fallback.RecentsState; import com.android.quickstep.util.RecentsAtomicAnimationFactory; +import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.views.OverviewActionsView; +import com.android.quickstep.views.SplitPlaceholderView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; @@ -105,8 +107,14 @@ public final class RecentsActivity extends StatefulActivity { mFallbackRecentsView = findViewById(R.id.overview_panel); mActionsView = findViewById(R.id.overview_actions_view); + SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder); + splitPlaceholderView.init( + new SplitSelectStateController( + SystemUiProxy.INSTANCE.get(this)) + ); + mDragLayer.recreateControllers(); - mFallbackRecentsView.init(mActionsView); + mFallbackRecentsView.init(mActionsView, splitPlaceholderView); } @Override diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index 8636130fd1..cd13200352 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -36,11 +36,16 @@ import androidx.annotation.RequiresApi; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.ResourceBasedOverride; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; +import com.android.quickstep.TaskShortcutFactory.SplitSelectSystemShortcut; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; @@ -57,11 +62,18 @@ import java.util.List; */ public class TaskOverlayFactory implements ResourceBasedOverride { - public static List getEnabledShortcuts(TaskView taskView) { + public static List getEnabledShortcuts(TaskView taskView, + DeviceProfile deviceProfile) { final ArrayList shortcuts = new ArrayList<>(); final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext()); for (TaskShortcutFactory menuOption : MENU_OPTIONS) { SystemShortcut shortcut = menuOption.getShortcut(activity, taskView); + if (menuOption == TaskShortcutFactory.SPLIT_SCREEN && + FeatureFlags.ENABLE_SPLIT_SELECT.get()) { + addSplitOptions(shortcuts, activity, taskView, deviceProfile); + continue; + } + if (shortcut != null) { shortcuts.add(shortcut); } @@ -91,6 +103,18 @@ public class TaskOverlayFactory implements ResourceBasedOverride { return shortcuts; } + + public static void addSplitOptions(List outShortcuts, + BaseDraggingActivity activity, TaskView taskView, DeviceProfile deviceProfile) { + PagedOrientationHandler orientationHandler = + taskView.getRecentsView().getPagedOrientationHandler(); + List positions = + orientationHandler.getSplitPositionOptions(deviceProfile); + for (SplitPositionOption option : positions) { + outShortcuts.add(new SplitSelectSystemShortcut(activity, taskView, option)); + } + } + public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) { return new TaskOverlay(thumbnailView); } diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java index d81f07f4c3..c06e9a90fa 100644 --- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java @@ -34,11 +34,13 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.StatsLogManager.LauncherEvent; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut.AppInfo; import com.android.launcher3.util.InstantAppResolver; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; @@ -58,7 +60,6 @@ import java.util.List; * Represents a system shortcut that can be shown for a recent task. */ public interface TaskShortcutFactory { - SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view); TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo()); @@ -93,6 +94,23 @@ public interface TaskShortcutFactory { } } + class SplitSelectSystemShortcut extends SystemShortcut { + private final TaskView mTaskView; + private SplitPositionOption mSplitPositionOption; + public SplitSelectSystemShortcut(BaseDraggingActivity target, TaskView taskView, + SplitPositionOption option) { + super(option.mIconResId, option.mTextResId, target, taskView.getItemInfo()); + mTaskView = taskView; + mSplitPositionOption = option; + setEnabled(taskView.getRecentsView().getTaskViewCount() > 1); + } + + @Override + public void onClick(View view) { + mTaskView.initiateSplitSelect(mSplitPositionOption); + } + } + class MultiWindowSystemShortcut extends SystemShortcut { private Handler mHandler; @@ -210,6 +228,16 @@ public interface TaskShortcutFactory { && (displayId == -1 || displayId == DEFAULT_DISPLAY); } + @Override + public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView taskView) { + SystemShortcut shortcut = super.getShortcut(activity, taskView); + if (FeatureFlags.ENABLE_SPLIT_SELECT.get()) { + // Disable if there's only one recent app for split screen + shortcut.setEnabled(taskView.getRecentsView().getTaskViewCount() > 1); + } + return shortcut; + } + @Override protected ActivityOptions makeLaunchOptions(Activity activity) { final ActivityCompat act = new ActivityCompat(activity); diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 2feeffa202..7a428ce637 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -23,6 +23,7 @@ import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncest import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.statehandlers.DepthController.DEPTH; import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; @@ -37,6 +38,7 @@ import android.content.ComponentName; import android.content.Context; import android.graphics.Matrix; import android.graphics.Matrix.ScaleToFit; +import android.graphics.Rect; import android.graphics.RectF; import android.os.Build; import android.view.View; @@ -295,6 +297,93 @@ public final class TaskViewUtils { } } + /** + * TODO: This doesn't animate at present. Feel free to blow out everyhing in this method + * if needed + * + * We could manually try to animate the just the bounds for the leashes we get back, but we try + * to do it through TaskViewSimulator(TVS) since that handles a lot of the recents UI stuff for + * us. + * + * First you have to call TVS#setPreview() to indicate which leash it will operate one + * Then operations happen in TVS#apply() on each frame callback. + * + * TVS uses DeviceProfile to try to figure out things like task height and such based on if the + * device is in multiWindowMode or not. It's unclear given the two calls to startTask() when the + * device is considered in multiWindowMode and things like insets and stuff change + * and calculations have to be adjusted in the animations for that + */ + public static void composeRecentsSplitLaunchAnimator(@NonNull AnimatorSet anim, + @NonNull TaskView v, @NonNull RemoteAnimationTargetCompat[] appTargets, + @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing, + @NonNull StateManager stateManager, @NonNull DepthController depthController, + int targetStage) { + PendingAnimation out = new PendingAnimation(RECENTS_LAUNCH_DURATION); + boolean isRunningTask = v.isRunningTask(); + TransformParams params = null; + TaskViewSimulator tvs = null; + RecentsView recentsView = v.getRecentsView(); + if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) { + params = recentsView.getLiveTileParams(); + tvs = recentsView.getLiveTileTaskViewSimulator(); + } + + boolean inLiveTileMode = + ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1; + final RemoteAnimationTargets targets = + new RemoteAnimationTargets(appTargets, wallpaperTargets, + inLiveTileMode ? MODE_CLOSING : MODE_OPENING); + + if (params == null) { + SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); + targets.addReleaseCheck(applier); + + params = new TransformParams() + .setSyncTransactionApplier(applier) + .setTargetSet(targets); + } + + Rect crop = new Rect(); + Context context = v.getContext(); + DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile(); + if (tvs == null && targets.apps.length > 0) { + tvs = new TaskViewSimulator(recentsView.getContext(), recentsView.getSizeStrategy()); + tvs.setDp(dp); + + // RecentsView never updates the display rotation until swipe-up so the value may + // be stale. Use the display value instead. + int displayRotation = DisplayController.getDefaultDisplay(recentsView.getContext()) + .getInfo().rotation; + tvs.getOrientationState().update(displayRotation, displayRotation); + + tvs.setPreview(targets.apps[targets.apps.length - 1]); + tvs.fullScreenProgress.value = 0; + tvs.recentsViewScale.value = 1; +// tvs.setScroll(startScroll); + + // Fade in the task during the initial 20% of the animation + out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, + clampToProgress(LINEAR, 0, 0.2f)); + } + + TaskViewSimulator topMostSimulator = null; + + if (tvs != null) { + out.setFloat(tvs.fullScreenProgress, + AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR); + out.setFloat(tvs.recentsViewScale, + AnimatedFloat.VALUE, tvs.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR); + out.setInt(tvs, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR); + + TaskViewSimulator finalTsv = tvs; + TransformParams finalParams = params; + out.addOnFrameCallback(() -> finalTsv.apply(finalParams)); + topMostSimulator = tvs; + } + + anim.play(out.buildAnim()); + } + public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing, diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 13f6137fb1..02fd5bb3f7 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -34,6 +34,7 @@ import com.android.quickstep.GestureState; import com.android.quickstep.RecentsActivity; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; +import com.android.quickstep.views.SplitPlaceholderView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task.TaskKey; @@ -56,8 +57,8 @@ public class FallbackRecentsView extends RecentsView } @Override - public void init(OverviewActionsView actionsView) { - super.init(actionsView); + public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) { + super.init(actionsView, splitPlaceholderView); setOverviewStateEnabled(true); setOverlayEnabled(true); } diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java new file mode 100644 index 0000000000..d9154edc8d --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -0,0 +1,151 @@ +/* + * Copyright 2021 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.quickstep.util; + +import android.animation.AnimatorSet; +import android.app.ActivityOptions; +import android.os.Handler; +import android.os.Looper; +import android.util.Pair; + +import androidx.annotation.Nullable; + +import com.android.launcher3.BaseActivity; +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.LauncherAnimationRunner; +import com.android.launcher3.WrappedAnimationRunnerImpl; +import com.android.launcher3.WrappedLauncherAnimationRunner; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; +import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.TaskViewUtils; +import com.android.quickstep.views.TaskView; +import com.android.systemui.shared.system.ActivityOptionsCompat; +import com.android.systemui.shared.system.RemoteAnimationAdapterCompat; +import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +/** + * Represent data needed for the transient state when user has selected one app for split screen + * and is in the process of either a) selecting a second app or b) exiting intention to invoke split + */ +public class SplitSelectStateController { + + private final SystemUiProxy mSystemUiProxy; + private TaskView mInitialTaskView; + private SplitPositionOption mInitialPosition; + + public SplitSelectStateController(SystemUiProxy systemUiProxy) { + mSystemUiProxy = systemUiProxy; + } + + /** + * To be called after first task selected + */ + public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption) { + mInitialTaskView = taskView; + mInitialPosition = positionOption; + } + + /** + * To be called after second task selected + */ + public void setSecondTaskId(TaskView taskView) { + // Assume initial mInitialTaskId is for top/left part of screen + WrappedAnimationRunnerImpl initialSplitRunnerWrapped = new SplitLaunchAnimationRunner( + mInitialTaskView, 0); + WrappedAnimationRunnerImpl secondarySplitRunnerWrapped = new SplitLaunchAnimationRunner( + taskView, 1); + RemoteAnimationRunnerCompat initialSplitRunner = new WrappedLauncherAnimationRunner( + new Handler(Looper.getMainLooper()), initialSplitRunnerWrapped, + true /* startAtFrontOfQueue */); + RemoteAnimationRunnerCompat secondarySplitRunner = new WrappedLauncherAnimationRunner( + new Handler(Looper.getMainLooper()), secondarySplitRunnerWrapped, + true /* startAtFrontOfQueue */); + ActivityOptions initialOptions = ActivityOptionsCompat.makeRemoteAnimation( + new RemoteAnimationAdapterCompat(initialSplitRunner, 300, 150)); + ActivityOptions secondaryOptions = ActivityOptionsCompat.makeRemoteAnimation( + new RemoteAnimationAdapterCompat(secondarySplitRunner, 300, 150)); + mSystemUiProxy.startTask(mInitialTaskView.getTask().key.id, mInitialPosition.mStageType, + mInitialPosition.mStagePosition, + /*null*/ initialOptions.toBundle()); + Pair compliment = getComplimentaryStageAndPosition(mInitialPosition); + mSystemUiProxy.startTask(taskView.getTask().key.id, compliment.first, + compliment.second, + /*null*/ secondaryOptions.toBundle()); + // After successful launch, call resetState + resetState(); + } + + @Nullable + public SplitPositionOption getActiveSplitPositionOption() { + return mInitialPosition; + } + + /** + * @return the opposite stage and position from the {@param position} provided as first and + * second object, respectively + * Ex. If position is has stage = Main and position = Top/Left, this will return + * Pair(stage=Side, position=Bottom/Left) + */ + private Pair getComplimentaryStageAndPosition(SplitPositionOption position) { + // Right now this is as simple as flipping between 0 and 1 + int complimentStageType = position.mStageType ^ 1; + int complimentStagePosition = position.mStagePosition ^ 1; + return new Pair<>(complimentStageType, complimentStagePosition); + } + + /** + * Remote animation runner for animation to launch an app. + */ + private class SplitLaunchAnimationRunner implements WrappedAnimationRunnerImpl { + + private final TaskView mV; + private final int mTargetState; + + SplitLaunchAnimationRunner(TaskView v, int targetState) { + mV = v; + mTargetState = targetState; + } + + @Override + public void onCreateAnimation(int transit, + RemoteAnimationTargetCompat[] appTargets, + RemoteAnimationTargetCompat[] wallpaperTargets, + RemoteAnimationTargetCompat[] nonAppTargets, + LauncherAnimationRunner.AnimationResult result) { + AnimatorSet anim = new AnimatorSet(); + BaseQuickstepLauncher activity = BaseActivity.fromContext(mV.getContext()); + TaskViewUtils.composeRecentsSplitLaunchAnimator(anim, mV, + appTargets, wallpaperTargets, true, activity.getStateManager(), + activity.getDepthController(), mTargetState); + result.setAnimation(anim, activity); + } + } + + + /** + * To be called if split select was cancelled + */ + public void resetState() { + mInitialTaskView = null; + mInitialPosition = null; + } + + public boolean isSplitSelectActive() { + return mInitialTaskView != null; + } +} diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index df1229ba05..6e8a5f13d3 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -104,6 +104,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { public final AnimatedFloat recentsViewScale = new AnimatedFloat(); public final AnimatedFloat fullScreenProgress = new AnimatedFloat(); public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat(); + public final AnimatedFloat recentsViewPrimaryTranslation = new AnimatedFloat(); public final AnimatedFloat gridProgress = new AnimatedFloat(); private final ScrollState mScrollState = new ScrollState(); @@ -347,6 +348,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y); mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE, recentsViewSecondaryTranslation.value); + mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE, + recentsViewPrimaryTranslation.value); applyWindowToHomeRotation(mMatrix); // Crop rect is the inverse of thumbnail matrix diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index ceb343d89f..9d31190eb6 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -86,8 +86,8 @@ public class LauncherRecentsView extends RecentsView } @Override - public void init(OverviewActionsView actionsView) { - super.init(actionsView); + public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) { + super.init(actionsView, splitPlaceholderView); setContentAlpha(0); } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index b9630259ef..3c5e403ada 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -26,6 +26,7 @@ import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRES import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK; +import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.mapToRange; import static com.android.launcher3.Utilities.squaredHypot; @@ -48,6 +49,7 @@ import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_RO import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS; import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS; +import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.LayoutTransition; import android.animation.LayoutTransition.TransitionListener; @@ -114,6 +116,7 @@ import com.android.launcher3.util.IntSet; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.OverScroller; import com.android.launcher3.util.ResourceBasedOverride.Overrides; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.Themes; import com.android.launcher3.util.ViewPool; import com.android.quickstep.AnimatedFloat; @@ -211,11 +214,17 @@ public abstract class RecentsView extends PagedView } }; + /** + * Even though {@link TaskView} has distinct offsetTranslationX/Y and resistance property, they + * are currently both used to apply secondary translation. Should their use cases change to be + * more specific, we'd want to create a similar FloatProperty just for a TaskView's + * offsetX/Y property + */ public static final FloatProperty TASK_SECONDARY_TRANSLATION = new FloatProperty("taskSecondaryTranslation") { @Override public void setValue(RecentsView recentsView, float v) { - recentsView.setTaskViewsSecondaryTranslation(v); + recentsView.setTaskViewsResistanceTranslation(v); } @Override @@ -224,6 +233,25 @@ public abstract class RecentsView extends PagedView } }; + /** + * Even though {@link TaskView} has distinct offsetTranslationX/Y and resistance property, they + * are currently both used to apply secondary translation. Should their use cases change to be + * more specific, we'd want to create a similar FloatProperty just for a TaskView's + * offsetX/Y property + */ + public static final FloatProperty TASK_PRIMARY_TRANSLATION = + new FloatProperty("taskPrimaryTranslation") { + @Override + public void setValue(RecentsView recentsView, float v) { + recentsView.setTaskViewsPrimaryTranslation(v); + } + + @Override + public Float get(RecentsView recentsView) { + return recentsView.mTaskViewsPrimaryTranslation; + } + }; + /** Same as normal SCALE_PROPERTY, but also updates page offsets that depend on this scale. */ public static final FloatProperty RECENTS_SCALE_PROPERTY = new FloatProperty("recentsScale") { @@ -234,7 +262,8 @@ public abstract class RecentsView extends PagedView view.mLastComputedTaskPushOutDistance = null; view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale; view.updatePageOffsets(); - view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation); + view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation); + view.setTaskViewsPrimaryTranslation(view.mTaskViewsPrimaryTranslation); } @Override @@ -306,6 +335,7 @@ public abstract class RecentsView extends PagedView private float mAdjacentPageOffset = 0; private float mTaskViewsSecondaryTranslation = 0; + private float mTaskViewsPrimaryTranslation = 0; // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid. private float mGridProgress = 0; private boolean mShowAsGrid; @@ -427,6 +457,28 @@ public abstract class RecentsView extends PagedView private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener; private Layout mEmptyTextLayout; + /** + * Placeholder view indicating where the first split screen selected app will be placed + */ + private SplitPlaceholderView mSplitPlaceholderView; + /** + * The first task that split screen selection was initiated with. When split select state is + * initialized, we create a + * {@link #createTaskDismissAnimation(TaskView, boolean, boolean, long)} for this TaskView but + * don't actually remove the task since the user might back out. As such, we also ensure this + * View doesn't go back into the {@link #mTaskViewPool}, see {@link #onViewRemoved(View)} + */ + private TaskView mSplitHiddenTaskView; + /** + * Keeps track of the index of the TaskView that split screen was initialized with so we know + * where to insert it back into list of taskViews in case user backs out of entering split + * screen. + * NOTE: This index is the index while {@link #mSplitHiddenTaskView} was a child of recentsView, + * this doesn't get adjusted to reflect the new child count after the taskView is dismissed/ + * removed from recentsView + */ + private int mSplitHiddenTaskViewIndex; + // Keeps track of the index where the first TaskView should be private int mTaskViewStartIndex = 0; private OverviewActionsView mActionsView; @@ -582,9 +634,19 @@ public abstract class RecentsView extends PagedView loadVisibleTaskData(); } - public void init(OverviewActionsView actionsView) { + public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) { mActionsView = actionsView; mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0); + mSplitPlaceholderView = splitPlaceholderView; + + } + + public SplitPlaceholderView getSplitPlaceholder() { + return mSplitPlaceholderView; + } + + public boolean isSplitSelectionActive() { + return mSplitPlaceholderView.getSplitController().isSplitSelectActive(); } @Override @@ -628,8 +690,9 @@ public abstract class RecentsView extends PagedView public void onViewRemoved(View child) { super.onViewRemoved(child); - // Clear the task data for the removed child if it was visible - if (child instanceof TaskView) { + // Clear the task data for the removed child if it was visible unless it's the initial + // taskview for entering split screen, we only pretend to dismiss the task + if (child instanceof TaskView && child != mSplitHiddenTaskView) { TaskView taskView = (TaskView) child; mHasVisibleTaskData.delete(taskView.getTask().key.id); mTaskViewPool.recycle(taskView); @@ -707,6 +770,9 @@ public abstract class RecentsView extends PagedView // Reset the running task when leaving overview since it can still have a reference to // its thumbnail mTmpRunningTask = null; + if (mSplitPlaceholderView.getSplitController().isSplitSelectActive()) { + cancelSplitSelect(false); + } } } @@ -1764,7 +1830,7 @@ public abstract class RecentsView extends PagedView // alpha is set to 0 so that it can be recycled in the view pool properly anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2); FloatProperty secondaryViewTranslate = - taskView.getDismissTaskTranslationProperty(); + taskView.getSecondaryDissmissTranslationProperty(); int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView); int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor(); @@ -1827,10 +1893,23 @@ public abstract class RecentsView extends PagedView offset += mIsRtl ? -scrollDiffPerPage : scrollDiffPerPage; } } + + // Additional offset for fake landscape, if the pinning happens to the right or + // left, we need to scroll all the tasks away from the direction of the splaceholder + // view + if (isSplitSelectionActive()) { + int splitPosition = getSplitPlaceholder().getSplitController() + .getActiveSplitPositionOption().mStagePosition; + int direction = mOrientationHandler + .getSplitTranslationDirectionFactor(splitPosition); + int splitOffset = mOrientationHandler.getSplitAnimationTranslation( + mSplitPlaceholderView.getHeight(), mActivity.getDeviceProfile()); + offset += direction * splitOffset; + } int scrollDiff = newScroll[i] - oldScroll[i] + offset; if (scrollDiff != 0) { FloatProperty translationProperty = child instanceof TaskView - ? ((TaskView) child).getFillDismissGapTranslationProperty() + ? ((TaskView) child).getPrimaryDismissTranslationProperty() : mOrientationHandler.getPrimaryViewTranslate(); ResourceProvider rp = DynamicResource.provider(mActivity); @@ -1908,6 +1987,12 @@ public abstract class RecentsView extends PagedView onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom()); } resetTaskVisuals(); + if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) { + // We want to keep the tasks translations in this temporary state + // after resetting the rest above + setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation); + setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation); + } mPendingAnimation = null; } }); @@ -2299,7 +2384,7 @@ public abstract class RecentsView extends PagedView return distanceToOffscreen * offsetProgress; } - private void setTaskViewsSecondaryTranslation(float translation) { + private void setTaskViewsResistanceTranslation(float translation) { mTaskViewsSecondaryTranslation = translation; for (int i = 0; i < getTaskViewCount(); i++) { TaskView task = getTaskViewAt(i); @@ -2308,6 +2393,15 @@ public abstract class RecentsView extends PagedView mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation; } + private void setTaskViewsPrimaryTranslation(float translation) { + mTaskViewsPrimaryTranslation = translation; + for (int i = 0; i < getTaskViewCount(); i++) { + TaskView task = getTaskViewAt(i); + task.getPrimaryDismissTranslationProperty().set(task, translation / getScaleY()); + } + mLiveTileTaskViewSimulator.recentsViewPrimaryTranslation.value = translation; + } + /** * TODO: Do not assume motion across X axis for adjacent page */ @@ -2325,6 +2419,111 @@ public abstract class RecentsView extends PagedView } } + public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) { + mSplitHiddenTaskView = taskView; + mSplitPlaceholderView.getSplitController().setInitialTaskSelect(taskView, + splitPositionOption); + mSplitHiddenTaskViewIndex = indexOfChild(taskView); + mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT); + } + + public PendingAnimation createSplitSelectInitAnimation() { + int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext()); + return createTaskDismissAnimation(mSplitHiddenTaskView, true, false, duration); + } + + public void confirmSplitSelect(TaskView taskView) { + mSplitPlaceholderView.getSplitController().setSecondTaskId(taskView); + resetTaskVisuals(); + setTranslationY(0); + } + + public PendingAnimation cancelSplitSelect(boolean animate) { + mSplitPlaceholderView.getSplitController().resetState(); + int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext()); + PendingAnimation pendingAnim = new PendingAnimation(duration); + if (!animate) { + resetFromSplitSelectionState(); + return pendingAnim; + } + + addViewInLayout(mSplitHiddenTaskView, mSplitHiddenTaskViewIndex, + mSplitHiddenTaskView.getLayoutParams()); + mSplitHiddenTaskView.setAlpha(0); + int[] oldScroll = new int[getChildCount()]; + getPageScrolls(oldScroll, false, + view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView); + + // x is correct, y is before tasks move up + int[] locationOnScreen = mSplitHiddenTaskView.getLocationOnScreen(); + int[] newScroll = new int[getChildCount()]; + getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC); + + boolean needsCurveUpdates = false; + for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) { + View child = getChildAt(i); + if (child == mSplitHiddenTaskView) { + + int left = newScroll[i] + getPaddingStart(); + int topMargin = mSplitHiddenTaskView.getThumbnailTopMargin(); + int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1]; + mSplitHiddenTaskView.layout(left, top, + left + mSplitHiddenTaskView.getWidth(), + top + mSplitHiddenTaskView.getHeight()); + pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, TRANSLATION_Y, + -top + mSplitPlaceholderView.getHeight() - topMargin)); + pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1)); + } else { + // If insertion is on last index (furthest from clear all), we directly add the view + // else we translate all views to the right of insertion index further right, + // ignore views to left + int scrollDiff = newScroll[i] - oldScroll[i]; + if (scrollDiff != 0) { + FloatProperty translationProperty = child instanceof TaskView + ? ((TaskView) child).getPrimaryDismissTranslationProperty() + : mOrientationHandler.getPrimaryViewTranslate(); + + ResourceProvider rp = DynamicResource.provider(mActivity); + SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END) + .setDampingRatio( + rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio)) + .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness)); + pendingAnim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff) + .setDuration(duration), ACCEL, sp); + needsCurveUpdates = true; + } + } + } + + if (needsCurveUpdates) { + pendingAnim.addOnFrameCallback(this::updateCurveProperties); + } + + pendingAnim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + resetFromSplitSelectionState(); + } + }); + + return pendingAnim; + } + + private void resetFromSplitSelectionState() { + mSplitHiddenTaskView.setTranslationY(0); + int pageToSnapTo = mCurrentPage; + if (mSplitHiddenTaskViewIndex <= pageToSnapTo) { + pageToSnapTo += 1; + } else { + pageToSnapTo = mSplitHiddenTaskViewIndex; + } + snapToPageImmediately(pageToSnapTo); + onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom()); + resetTaskVisuals(); + mSplitHiddenTaskView = null; + mSplitHiddenTaskViewIndex = -1; + } + private void updateDeadZoneRects() { // Get the deadzone rect surrounding the clear all button to not dismiss overview to home mClearAllButtonDeadZoneRect.setEmpty(); diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java new file mode 100644 index 0000000000..fb9be814f1 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java @@ -0,0 +1,55 @@ +/* + * Copyright 2021 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.quickstep.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.FloatProperty; +import android.view.View; + +import com.android.quickstep.util.SplitSelectStateController; + +public class SplitPlaceholderView extends View { + + public static final FloatProperty ALPHA_FLOAT = + new FloatProperty("SplitViewAlpha") { + @Override + public void setValue(SplitPlaceholderView splitPlaceholderView, float v) { + splitPlaceholderView.setVisibility(v != 0 ? VISIBLE : GONE); + splitPlaceholderView.setAlpha(v); + } + + @Override + public Float get(SplitPlaceholderView splitPlaceholderView) { + return splitPlaceholderView.getAlpha(); + } + }; + + private SplitSelectStateController mSplitController; + + public SplitPlaceholderView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void init(SplitSelectStateController controller) { + this.mSplitController = controller; + } + + public SplitSelectStateController getSplitController() { + return mSplitController; + } +} diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java index fe7ece216a..a5b7a5b575 100644 --- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java @@ -186,7 +186,8 @@ public class TaskMenuView extends AbstractFloatingView { mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask())); mTaskName.setOnClickListener(v -> close(true)); - TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption); + TaskOverlayFactory.getEnabledShortcuts(taskView, mActivity.getDeviceProfile()) + .forEach(this::addMenuOption); } private void addMenuOption(SystemShortcut menuOption) { @@ -196,6 +197,8 @@ public class TaskMenuView extends AbstractFloatingView { menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text)); LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams(); mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp); + menuOptionView.setEnabled(menuOption.isEnabled()); + menuOptionView.setAlpha(menuOption.isEnabled() ? 1 : 0.5f); menuOptionView.setOnClickListener(view -> { if (LIVE_TILE.get()) { RecentsView recentsView = mTaskView.getRecentsView(); diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index eace0f8015..cd8ea7675f 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -28,6 +28,7 @@ import static android.view.Surface.ROTATION_90; import static android.widget.Toast.LENGTH_SHORT; import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION; +import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT; import static com.android.launcher3.Utilities.comp; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; @@ -84,6 +85,7 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.RunnableList; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; import com.android.launcher3.util.TransformingTouchDelegate; import com.android.launcher3.util.ViewPool.Reusable; import com.android.quickstep.RecentsModel; @@ -345,7 +347,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { }); anim.start(); } else { - launchTaskAnimated(); + if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) { + // User tapped to select second split screen app + getRecentsView().confirmSplitSelect(this); + } else { + launchTaskAnimated(); + } } mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo()) .log(LAUNCHER_TASK_LAUNCH_TAP); @@ -591,6 +598,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } private boolean showTaskMenu() { + if (getRecentsView().mActivity.isInState(OVERVIEW_SPLIT_SELECT)) { + // Don't show menu when selecting second split screen app + return true; + } + if (!getRecentsView().isClearAllHidden()) { getRecentsView().snapToPage(getRecentsView().indexOfChild(this)); } else { @@ -619,6 +631,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } + public int getThumbnailTopMargin() { + return (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); + } + public void setOrientationState(RecentsOrientedState orientationState) { PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler(); boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; @@ -998,12 +1014,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { return Utilities.mapRange(progress, 0, endTranslation); } - public FloatProperty getFillDismissGapTranslationProperty() { + public FloatProperty getPrimaryDismissTranslationProperty() { return getPagedOrientationHandler().getPrimaryValue( DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y); } - public FloatProperty getDismissTaskTranslationProperty() { + public FloatProperty getSecondaryDissmissTranslationProperty() { return getPagedOrientationHandler().getSecondaryValue( DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y); } @@ -1081,7 +1097,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { getContext().getText(R.string.accessibility_close))); final Context context = getContext(); - for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) { + for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, + mActivity.getDeviceProfile())) { info.addAction(s.createAccessibilityAction(context)); } @@ -1113,7 +1130,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { return true; } - for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) { + for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, + mActivity.getDeviceProfile())) { if (s.hasHandlerForAction(action)) { s.onClick(this); return true; @@ -1274,6 +1292,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mSnapshotView.setOverlayEnabled(overlayEnabled); } + public void initiateSplitSelect(SplitPositionOption splitPositionOption) { + RecentsView rv = getRecentsView(); + getMenuView().close(false); + rv.initiateSplitSelect(this, splitPositionOption); + } + /** * We update and subsequently draw these in {@link #setFullscreenProgress(float)}. */ diff --git a/quickstep/res/drawable/ic_split_screen.xml b/res/drawable/ic_split_screen.xml similarity index 100% rename from quickstep/res/drawable/ic_split_screen.xml rename to res/drawable/ic_split_screen.xml diff --git a/res/values/strings.xml b/res/values/strings.xml index 351182d8a6..0600cae271 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -38,6 +38,13 @@ Home + + + Split screen + Pin to top + Pin to left + Pin to right + Touch & hold to move a widget. diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 21c40efe30..aa97450a2b 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -22,6 +22,7 @@ import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_OR import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL; +import static com.android.launcher3.testing.TestProtocol.OVERVIEW_SPLIT_SELECT_ORDINAL; import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL; @@ -60,6 +61,7 @@ public abstract class LauncherState implements BaseState { public static final int TASKBAR = 1 << 7; public static final int CLEAR_ALL_BUTTON = 1 << 8; public static final int WORKSPACE_PAGE_INDICATOR = 1 << 9; + public static final int SPLIT_PLACHOLDER_VIEW = 1 << 10; /** Mask of all the items that are contained in the apps view. */ public static final int APPS_VIEW_ITEM_MASK = @@ -126,6 +128,8 @@ public abstract class LauncherState implements BaseState { OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL); public static final LauncherState BACKGROUND_APP = OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL); + public static final LauncherState OVERVIEW_SPLIT_SELECT = + OverviewState.newSplitSelectState(OVERVIEW_SPLIT_SELECT_ORDINAL); public final int ordinal; @@ -240,6 +244,14 @@ public abstract class LauncherState implements BaseState { return false; } + /** + * For this state, how much additional vertical translation there should be for each of the + * child TaskViews. + */ + public float getOverviewSecondaryTranslation(Launcher launcher) { + return 0; + } + /** * The amount of blur and wallpaper zoom to apply to the background of either the app * or Launcher surface in this state. Should be a number between 0 and 1, inclusive. diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 3657e892ad..48e41d5fda 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -205,6 +205,9 @@ public final class FeatureFlags { "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. " + "Only applicable on large screen devices."); + public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag( + "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI"); + public static void initialize(Context context) { synchronized (sDebugFlags) { for (DebugFlag flag : sDebugFlags) { diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 577fe4afaa..e5424cfe0a 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -45,6 +45,11 @@ public abstract class SystemShortcut extends Ite protected final T mTarget; protected final ItemInfo mItemInfo; + /** + * Indicates if it's invokable or not through some disabled UI + */ + private boolean isEnabled = true; + public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo) { mIconResId = iconResId; mLabelResId = labelResId; @@ -83,6 +88,14 @@ public abstract class SystemShortcut extends Ite mAccessibilityActionId, context.getText(mLabelResId)); } + public void setEnabled(boolean enabled) { + isEnabled = enabled; + } + + public boolean isEnabled() { + return isEnabled; + } + public boolean hasHandlerForAction(int action) { return mAccessibilityActionId == action; } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 7cb6e34a41..f34bff6994 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -32,6 +32,7 @@ public final class TestProtocol { public static final int ALL_APPS_STATE_ORDINAL = 5; public static final int BACKGROUND_APP_STATE_ORDINAL = 6; public static final int HINT_STATE_ORDINAL = 7; + public static final int OVERVIEW_SPLIT_SELECT_ORDINAL = 8; public static final String TAPL_EVENTS_TAG = "TaplEvents"; public static final String SEQUENCE_MAIN = "Main"; public static final String SEQUENCE_TIS = "TIS"; @@ -55,6 +56,8 @@ public final class TestProtocol { return "Background"; case HINT_STATE_ORDINAL: return "Hint"; + case OVERVIEW_SPLIT_SELECT_ORDINAL: + return "OverviewSplitSelect"; default: return "Unknown"; } diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 8a64f3d7b5..c1cf0c8ec2 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -21,6 +21,10 @@ import static android.widget.ListPopupWindow.WRAP_CONTENT; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_SIDE; import android.content.res.Resources; import android.graphics.PointF; @@ -36,8 +40,13 @@ import android.widget.LinearLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.PagedView; +import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.OverScroller; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; + +import java.util.ArrayList; +import java.util.List; public class LandscapePagedViewHandler implements PagedOrientationHandler { @@ -211,6 +220,20 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { return 1; } + @Override + public int getSplitTranslationDirectionFactor(int stagePosition) { + if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) { + return -1; + } else { + return 1; + } + } + + @Override + public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) { + return translationOffset; + } + @Override public float getTaskMenuX(float x, View thumbnailView) { return thumbnailView.getMeasuredWidth() + x; @@ -282,4 +305,23 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) { return rect.left; } + + @Override + public List getSplitPositionOptions(DeviceProfile dp) { + List options = new ArrayList<>(2); + // Add left/right options where left => position top, right => position bottom + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_left, + STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_right, + STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_SIDE)); + return options; + } + + @Override + public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, + DeviceProfile deviceProfile) { + return primary; + } } diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java index e1cec87df7..fcfa205ed2 100644 --- a/src/com/android/launcher3/touch/PagedOrientationHandler.java +++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java @@ -32,6 +32,10 @@ import android.widget.LinearLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.PagedView; import com.android.launcher3.util.OverScroller; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; +import com.android.launcher3.util.SplitConfigurationOptions.StagePosition; + +import java.util.List; /** * Abstraction layer to separate horizontal and vertical specific implementations @@ -75,6 +79,8 @@ public interface PagedOrientationHandler { int getScrollOffsetEnd(View view, Rect insets); int getPrimaryTranslationDirectionFactor(); int getSecondaryTranslationDirectionFactor(); + int getSplitTranslationDirectionFactor(@StagePosition int stagePosition); + int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp); ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild); void setMaxScroll(AccessibilityEvent event, int maxScroll); boolean getRecentsRtlSetting(Resources resources); @@ -95,6 +101,9 @@ public interface PagedOrientationHandler { int getTaskMenuLayoutOrientation(boolean canRecentsActivityRotate, LinearLayout taskMenuLayout); void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp); int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect); + List getSplitPositionOptions(DeviceProfile dp); + FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, + DeviceProfile deviceProfile); // The following are only used by TaskViewTouchHandler. /** @return Either VERTICAL or HORIZONTAL. */ diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index bcaf5f444d..2bc2dc7d93 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -19,6 +19,10 @@ package com.android.launcher3.touch; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_SIDE; import android.content.res.Resources; import android.graphics.PointF; @@ -34,8 +38,13 @@ import android.widget.LinearLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.PagedView; +import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.OverScroller; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; + +import java.util.ArrayList; +import java.util.List; public class PortraitPagedViewHandler implements PagedOrientationHandler { @@ -207,6 +216,23 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { return -1; } + @Override + public int getSplitTranslationDirectionFactor(int stagePosition) { + if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) { + return -1; + } else { + return 1; + } + } + + @Override + public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) { + if (dp.isLandscape) { + return translationOffset; + } + return 0; + } + @Override public float getTaskMenuX(float x, View thumbnailView) { return x; @@ -277,4 +303,35 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) { return dp.heightPx - rect.bottom; } + + @Override + public List getSplitPositionOptions(DeviceProfile dp) { + List options = new ArrayList<>(2); + // TODO: Add in correct icons + if (dp.isLandscape) { // or seascape + // Add left/right options + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_left, + STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_right, + STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_SIDE)); + } else { + // Only add top option + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_top, + STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); + } + return options; + } + + @Override + public FloatProperty getSplitSelectTaskOffset(FloatProperty primary, FloatProperty secondary, + DeviceProfile dp) { + if (dp.isLandscape) { // or seascape + return primary; + } else { + return secondary; + } + } } diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java index 54af029266..b5252f73ea 100644 --- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -17,6 +17,10 @@ package com.android.launcher3.touch; import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_SIDE; import android.content.res.Resources; import android.graphics.PointF; @@ -25,7 +29,12 @@ import android.view.Surface; import android.view.View; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption; + +import java.util.ArrayList; +import java.util.List; public class SeascapePagedViewHandler extends LandscapePagedViewHandler { @@ -34,6 +43,20 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { return -1; } + @Override + public int getSplitTranslationDirectionFactor(int stagePosition) { + if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) { + return -1; + } else { + return 1; + } + } + + @Override + public int getSplitAnimationTranslation(int translationOffset, DeviceProfile dp) { + return translationOffset; + } + @Override public boolean getRecentsRtlSetting(Resources resources) { return Utilities.isRtl(resources); @@ -71,6 +94,19 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { return dp.widthPx - rect.right; } + @Override + public List getSplitPositionOptions(DeviceProfile dp) { + List options = new ArrayList<>(2); + // Add left/right options where left => position bottom, right => position top + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_left, + STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_SIDE)); + options.add(new SplitPositionOption( + R.drawable.ic_split_screen, R.string.split_screen_position_right, + STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN)); + return options; + } + /* ---------- The following are only used by TaskViewTouchHandler. ---------- */ @Override diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java new file mode 100644 index 0000000000..573c8bd367 --- /dev/null +++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021 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.util; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import androidx.annotation.IntDef; + +import java.lang.annotation.Retention; + +public final class SplitConfigurationOptions { + + /////////////////////////////////// + // Taken from + // frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java + /** + * Stage position isn't specified normally meaning to use what ever it is currently set to. + */ + public static final int STAGE_POSITION_UNDEFINED = -1; + /** + * Specifies that a stage is positioned at the top half of the screen if + * in portrait mode or at the left half of the screen if in landscape mode. + */ + public static final int STAGE_POSITION_TOP_OR_LEFT = 0; + + /** + * Specifies that a stage is positioned at the bottom half of the screen if + * in portrait mode or at the right half of the screen if in landscape mode. + */ + public static final int STAGE_POSITION_BOTTOM_OR_RIGHT = 1; + + @Retention(SOURCE) + @IntDef({STAGE_POSITION_UNDEFINED, STAGE_POSITION_TOP_OR_LEFT, STAGE_POSITION_BOTTOM_OR_RIGHT}) + public @interface StagePosition {} + + /** + * Stage type isn't specified normally meaning to use what ever the default is. + * E.g. exit split-screen and launch the app in fullscreen. + */ + public static final int STAGE_TYPE_UNDEFINED = -1; + /** + * The main stage type. + */ + public static final int STAGE_TYPE_MAIN = 0; + + /** + * The side stage type. + */ + public static final int STAGE_TYPE_SIDE = 1; + + @IntDef({STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, STAGE_TYPE_SIDE}) + public @interface StageType {} + /////////////////////////////////// + + public static class SplitPositionOption { + public final int mIconResId; + public final int mTextResId; + @StagePosition + public final int mStagePosition; + + @StageType + public final int mStageType; + + public SplitPositionOption(int iconResId, int textResId, int stagePosition, int stageType) { + mIconResId = iconResId; + mTextResId = textResId; + mStagePosition = stagePosition; + mStageType = stageType; + } + } +} diff --git a/src/com/android/launcher3/util/ViewPool.java b/src/com/android/launcher3/util/ViewPool.java index 5b33f1849e..e413d7ff9c 100644 --- a/src/com/android/launcher3/util/ViewPool.java +++ b/src/com/android/launcher3/util/ViewPool.java @@ -58,7 +58,7 @@ public class ViewPool { Preconditions.assertUIThread(); Handler handler = new Handler(); - // LayoutInflater is not thread save as it maintains a global variable 'mConstructorArgs'. + // LayoutInflater is not thread safe as it maintains a global variable 'mConstructorArgs'. // Create a different copy to use on the background thread. LayoutInflater inflater = mInflater.cloneInContext(mInflater.getContext()); diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java index da5a94f6fd..e85e505f35 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java @@ -49,4 +49,11 @@ public class OverviewState extends LauncherState { public static OverviewState newModalTaskState(int id) { return new OverviewState(id); } + + /** + * New Overview substate that represents the overview in modal mode (one task shown on its own) + */ + public static OverviewState newSplitSelectState(int id) { + return new OverviewState(id); + } }