Merge "launcher: use a different task menu for foldables" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index a83f51a..51d7914 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1558,7 +1558,8 @@
View workspaceView = findWorkspaceView(appTargets);
boolean isWorkspaceViewVisible = workspaceView != null
- && !mLauncher.isInState(LauncherState.ALL_APPS);
+ && !mLauncher.isInState(LauncherState.ALL_APPS)
+ && !mLauncher.getWorkspace().isOverlayShown();
boolean playFallBackAnimation = !isWorkspaceViewVisible
&& (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|| mLauncher.isForceInvisible());
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index a0a940b..45b2081 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -122,6 +122,7 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Arrays;
import java.util.function.Consumer;
/**
@@ -976,12 +977,13 @@
}
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
}
- if (mStateCallback.hasStates(STATE_START_NEW_TASK)
- && appearedTaskTarget.taskId == mGestureState.getLastStartedTaskId()) {
+ boolean hasStartedTaskBefore = Arrays.stream(appearedTaskTarget).anyMatch(
+ targetCompat -> targetCompat.taskId == mGestureState.getLastStartedTaskId());
+ if (mStateCallback.hasStates(STATE_START_NEW_TASK) && hasStartedTaskBefore) {
reset();
return true;
}
@@ -1809,13 +1811,8 @@
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
- boolean isOldTaskSplit = LauncherSplitScreenListener.INSTANCE.getNoCreate()
- .getRunningSplitTaskIds().length > 0;
nextTask.launchTask(success -> {
resultCallback.accept(success);
- if (isOldTaskSplit) {
- SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(taskId);
- }
if (success) {
if (hasTaskPreviouslyAppeared) {
onRestartPreviouslyAppearedTask();
@@ -1871,9 +1868,9 @@
}
@Override
- public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
if (mRecentsAnimationController != null) {
- if (handleTaskAppeared(appearedTaskTarget)) {
+ if (handleTaskAppeared(appearedTaskTargets)) {
mRecentsAnimationController.finish(false /* toRecents */,
null /* onFinishComplete */);
mActivityInterface.onLaunchTaskSuccess();
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 834bc55..f9b749e 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -306,7 +306,6 @@
* Calculates the overview grid size for the provided device configuration.
*/
public final void calculateGridSize(Context context, DeviceProfile dp, Rect outRect) {
- Resources res = context.getResources();
Rect insets = dp.getInsets();
int topMargin = dp.overviewTaskThumbnailTopMarginPx;
int bottomMargin = getOverviewActionsHeight(context, dp);
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index fed5ae5..a82137e 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -147,7 +147,7 @@
}
@Override
- protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {
if (mActiveAnimationFactory != null
&& mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) {
mActiveAnimationFactory = null;
@@ -260,7 +260,8 @@
}
}
- public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+ RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) {
RemoteAnimationTargets targets = new RemoteAnimationTargets(
new RemoteAnimationTargetCompat[] {appearedTaskTarget},
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 79dd633..c5f4a53 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -27,12 +27,14 @@
import androidx.annotation.VisibleForTesting;
+import com.android.quickstep.util.GroupTask;
import com.android.launcher3.util.LooperExecutor;
-import com.android.systemui.shared.recents.model.GroupTask;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.util.StagedSplitBounds;
import java.util.ArrayList;
import java.util.Collections;
@@ -192,12 +194,23 @@
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
task2.setLastSnapshotData(taskInfo2);
}
- allTasks.add(new GroupTask(task1, task2));
+ final SplitConfigurationOptions.StagedSplitBounds launcherSplitBounds =
+ convertSplitBounds(rawTask.mStagedSplitBounds);
+ allTasks.add(new GroupTask(task1, task2, launcherSplitBounds));
}
return allTasks;
}
+ private SplitConfigurationOptions.StagedSplitBounds convertSplitBounds(
+ StagedSplitBounds shellSplitBounds) {
+ return shellSplitBounds == null ?
+ null :
+ new SplitConfigurationOptions.StagedSplitBounds(
+ shellSplitBounds.leftTopBounds, shellSplitBounds.rightBottomBounds,
+ shellSplitBounds.leftTopTaskId, shellSplitBounds.rightBottomTaskId);
+ }
+
private ArrayList<GroupTask> copyOf(ArrayList<GroupTask> tasks) {
ArrayList<GroupTask> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 750985a..dd6392c 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -136,10 +136,10 @@
@BinderThread
@Override
- public void onTaskAppeared(RemoteAnimationTargetCompat app) {
+ public void onTasksAppeared(RemoteAnimationTargetCompat[] apps) {
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> {
for (RecentsAnimationListener listener : getListeners()) {
- listener.onTaskAppeared(app);
+ listener.onTasksAppeared(apps);
}
});
}
@@ -177,6 +177,6 @@
/**
* Callback made when a task started from the recents is ready for an app transition.
*/
- default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {}
+ default void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) {}
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 71153c6..ac97dd6 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -34,7 +34,7 @@
import com.android.launcher3.icons.IconProvider.IconChangeListener;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.recents.model.GroupTask;
+import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 5f2b49d..b031c47 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -20,7 +20,7 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.TaskViewSimulator;
@@ -33,7 +33,7 @@
*/
public class RemoteTargetGluer {
private RemoteTargetHandle[] mRemoteTargetHandles;
- private SplitConfigurationOptions.StagedSplitBounds mStagedSplitBounds;
+ private StagedSplitBounds mStagedSplitBounds;
/**
* Use this constructor if remote targets are split-screen independent
@@ -85,13 +85,21 @@
/**
* Similar to {@link #assignTargets(RemoteAnimationTargets)}, except this matches the
- * apps in targets.apps to that of the split screened tasks. If split screen is active, then
- * {@link #mRemoteTargetHandles} index 0 will be the left/top task, index one right/bottom
+ * apps in targets.apps to that of the _active_ split screened tasks.
+ * See {@link #assignTargetsForSplitScreen(RemoteAnimationTargets, int[])}
*/
public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets) {
int[] splitIds = LauncherSplitScreenListener.INSTANCE.getNoCreate()
.getRunningSplitTaskIds();
+ return assignTargetsForSplitScreen(targets, splitIds);
+ }
+ /**
+ * Assigns the provided splitIDs to the {@link #mRemoteTargetHandles}, with index 0 will beint
+ * the left/top task, index 1 right/bottom
+ */
+ public RemoteTargetHandle[] assignTargetsForSplitScreen(RemoteAnimationTargets targets,
+ int[] splitIds) {
RemoteAnimationTargetCompat primaryTaskTarget;
RemoteAnimationTargetCompat secondaryTaskTarget;
if (mRemoteTargetHandles.length == 1) {
@@ -108,9 +116,9 @@
primaryTaskTarget = targets.findTask(splitIds[0]);
secondaryTaskTarget = targets.findTask(splitIds[1]);
- mStagedSplitBounds = new SplitConfigurationOptions.StagedSplitBounds(
+ mStagedSplitBounds = new StagedSplitBounds(
primaryTaskTarget.screenSpaceBounds,
- secondaryTaskTarget.screenSpaceBounds);
+ secondaryTaskTarget.screenSpaceBounds, splitIds[0], splitIds[1]);
mRemoteTargetHandles[0].mTransformParams.setTargetSet(
createRemoteAnimationTargetsForTarget(primaryTaskTarget, targets));
mRemoteTargetHandles[0].mTaskViewSimulator.setPreview(primaryTaskTarget,
@@ -135,7 +143,7 @@
return mRemoteTargetHandles;
}
- public SplitConfigurationOptions.StagedSplitBounds getStagedSplitBounds() {
+ public StagedSplitBounds getStagedSplitBounds() {
return mStagedSplitBounds;
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index a875b69..4239739 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -572,53 +572,6 @@
mPendingSplitScreenListener = null;
}
- public void setSideStageVisibility(boolean visible) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.setSideStageVisibility(visible);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call setSideStageVisibility");
- }
- }
- }
-
- /**
- * To be called whenever the user exits out of split screen apps (either by launching another
- * app or by swiping home)
- * @param topTaskId The taskId of the new app that was launched. System will then move this task
- * to the front of what the user sees while removing all other split stages.
- * If swiping to home (or there is no task to put at the top), can pass in -1.
- */
- public void exitSplitScreen(int topTaskId) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.exitSplitScreen(topTaskId);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call exitSplitScreen");
- }
- }
- }
-
- public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.exitSplitScreenOnHide(exitSplitScreenOnHide);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call exitSplitScreen");
- }
- }
- }
-
- public void startTask(int taskId, int stage, int position, Bundle options) {
- if (mSplitScreen != null) {
- try {
- mSplitScreen.startTask(taskId, stage, position, options);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call startTask");
- }
- }
- }
-
/** Start multiple tasks in split-screen simultaneously. */
public void startTasks(int mainTaskId, Bundle mainOptions, int sideTaskId, Bundle sideOptions,
@SplitConfigurationOptions.StagePosition int sidePosition,
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index ed7f0b3..e69330a 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -147,16 +147,16 @@
}
@Override
- public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ public void onTasksAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) {
+ RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0];
BaseActivityInterface activityInterface = mLastGestureState.getActivityInterface();
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityInterface.isInLiveTileMode()
&& activityInterface.getCreatedActivity() != null) {
RecentsView recentsView =
activityInterface.getCreatedActivity().getOverviewPanel();
if (recentsView != null) {
- RemoteAnimationTargetCompat[] apps = new RemoteAnimationTargetCompat[1];
- apps[0] = appearedTaskTarget;
- recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId, apps,
+ recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
+ appearedTaskTargets,
new RemoteAnimationTargetCompat[0] /* wallpaper */,
new RemoteAnimationTargetCompat[0] /* nonApps */);
return;
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 15729e1..67a94cc 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -177,8 +177,8 @@
} else {
RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
recentsView.getSizeStrategy(), targets);
- if (recentsViewHandles != null && recentsViewHandles.length > 1) {
- remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets);
+ if (v.containsMultipleTasks()) {
+ remoteTargetHandles = gluer.assignTargetsForSplitScreen(targets, v.getTaskIds());
} else {
remoteTargetHandles = gluer.assignTargets(targets);
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 48315d0..eff59e2 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -42,7 +42,7 @@
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.GroupTask;
+import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -145,9 +145,10 @@
RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
if (mHomeTaskInfo != null && runningTaskInfo != null &&
mHomeTaskInfo.taskId == runningTaskInfo.taskId
- && getTaskViewCount() == 0) {
+ && getTaskViewCount() == 0 && mLoadPlanEverApplied) {
// Do not add a stub task if we are running over home with empty recents, so that we
// show the empty recents message instead of showing a stub task and later removing it.
+ // Ignore empty task signal if applyLoadPlan has never run.
return false;
}
return super.shouldAddStubTaskView(runningTaskInfos);
@@ -174,7 +175,7 @@
newList.addAll(taskGroups);
newList.add(new GroupTask(
Task.from(new TaskKey(mHomeTaskInfo), mHomeTaskInfo, false),
- null));
+ null, null));
taskGroups = newList;
}
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index 033fd85..71dca66 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -153,10 +153,8 @@
ObjectAnimator anim = mProgress.animateToValue(endValue);
anim.setDuration(duration).setInterpolator(scrollInterpolatorForVelocity(velocity));
- if (mRecentsAnimationController != null) {
- anim.addListener(AnimatorListeners.forSuccessCallback(
- () -> mStateCallback.setState(STATE_FLING_FINISHED)));
- }
+ anim.addListener(AnimatorListeners.forSuccessCallback(
+ () -> mStateCallback.setState(STATE_FLING_FINISHED)));
anim.start();
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 272a9a1..965c1bc 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -136,6 +136,9 @@
protected void onPause() {
super.onPause();
clearBinderOverride();
+ if (mSwipeProgress.value >= 1) {
+ finishAndRemoveTask();
+ }
}
private void clearBinderOverride() {
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
new file mode 100644
index 0000000..e2563e3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * A {@link Task} container that can contain one or two tasks, depending on if the two tasks
+ * are represented as an app-pair in the recents task list.
+ */
+public class GroupTask {
+ public @NonNull Task task1;
+ public @Nullable Task task2;
+ public @Nullable StagedSplitBounds mStagedSplitBounds;
+
+ public GroupTask(@NonNull Task t1, @Nullable Task t2,
+ @Nullable StagedSplitBounds stagedSplitBounds) {
+ task1 = t1;
+ task2 = t2;
+ mStagedSplitBounds = stagedSplitBounds;
+ }
+
+ public GroupTask(@NonNull GroupTask group) {
+ task1 = new Task(group.task1);
+ task2 = group.task2 != null
+ ? new Task(group.task2)
+ : null;
+ mStagedSplitBounds = group.mStagedSplitBounds;
+ }
+
+ public boolean containsTask(int taskId) {
+ return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
+ }
+
+ public boolean hasMultipleTasks() {
+ return task2 != null;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
index fa4cddc..99efb39 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherSplitScreenListener.java
@@ -149,7 +149,6 @@
return;
}
- SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(-1);
mPersistentGroupedIds = EMPTY_ARRAY;
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 4c300ec..e1afa97 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -56,6 +56,7 @@
private Task mInitialTask;
private Task mSecondTask;
private Rect mInitialBounds;
+ private boolean mRecentsAnimationRunning;
public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
mHandler = handler;
@@ -113,6 +114,10 @@
return mStagePosition;
}
+ public void setRecentsAnimationRunning(boolean running) {
+ this.mRecentsAnimationRunning = running;
+ }
+
/**
* Requires Shell Transitions
*/
@@ -172,7 +177,9 @@
public void onAnimationCancelled() {
postAsyncCallback(mHandler, () -> {
if (mSuccessCallback != null) {
- mSuccessCallback.accept(false);
+ // Launching legacy tasks while recents animation is running will always cause
+ // onAnimationCancelled to be called (should be fixed w/ shell transitions?)
+ mSuccessCallback.accept(mRecentsAnimationRunning);
}
resetState();
});
@@ -187,6 +194,7 @@
mSecondTask = null;
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
mInitialBounds = null;
+ mRecentsAnimationRunning = false;
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 977c696..280b5d7 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import static com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
@@ -41,6 +40,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index a79b1f3..eace227 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -14,6 +14,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.RunnableList;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.quickstep.RecentsModel;
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 5a8cbc1..5c0b50c 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -127,6 +127,9 @@
* @param callbacks for callbacks, or {@code null} to clear the listener.
*/
public void setCallbacks(T callbacks) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_SCREENSHOT, "OverviewActionsView setCallbacks: " + callbacks);
+ }
mCallbacks = callbacks;
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c721e50..1d01b7a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -137,8 +137,8 @@
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.RunnableList;
-import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
import com.android.launcher3.util.ViewPool;
@@ -157,7 +157,6 @@
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.ViewUtils;
-import com.android.quickstep.util.LauncherSplitScreenListener;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
@@ -167,7 +166,7 @@
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.VibratorWrapper;
import com.android.systemui.plugins.ResourceProvider;
-import com.android.systemui.shared.recents.model.GroupTask;
+import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -573,6 +572,7 @@
// Keeps track of task id whose visual state should not be reset
private int mIgnoreResetTaskId = -1;
+ protected boolean mLoadPlanEverApplied;
// Variables for empty state
private final Drawable mEmptyIcon;
@@ -598,7 +598,7 @@
*/
private TaskView mSplitHiddenTaskView;
private TaskView mSecondSplitHiddenTaskView;
- private SplitConfigurationOptions.StagedSplitBounds mSplitBoundsConfig;
+ private StagedSplitBounds mSplitBoundsConfig;
private final Toast mSplitToast = Toast.makeText(getContext(),
R.string.toast_split_select_app, Toast.LENGTH_SHORT);
private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
@@ -664,9 +664,8 @@
mClearAllButton.setOnClickListener(this::dismissAllTasks);
mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */,
10 /* initial size */);
- // There's only one pair of grouped tasks we can envision at the moment
mGroupedTaskViewPool = new ViewPool<>(context, this,
- R.layout.task_grouped, 2 /* max size */, 1 /* initial size */);
+ R.layout.task_grouped, 20 /* max size */, 10 /* initial size */);
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
@@ -1070,6 +1069,10 @@
}
public TaskView getTaskViewByTaskId(int taskId) {
+ if (taskId == -1) {
+ return null;
+ }
+
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = getTaskViewAt(i);
int[] taskIds = taskView.getTaskIds();
@@ -1322,72 +1325,34 @@
TaskView ignoreResetTaskView =
mIgnoreResetTaskId == -1 ? null : getTaskViewByTaskId(mIgnoreResetTaskId);
- int[] splitTaskIds =
- LauncherSplitScreenListener.INSTANCE.getNoCreate().getPersistentSplitIds();
- int requiredGroupTaskViews = splitTaskIds.length / 2;
-
- // TODO(b/202740477): Update once grouped tasks are returned
- // Subtract half the number of split tasks and not total number because we've already
- // added a GroupedTaskView when swipe up gesture happens.
- // This will need to change if we start showing GroupedTaskViews during swipe up from home
- int requiredTaskViewCount = taskGroups.size() - requiredGroupTaskViews;
-
- if (getTaskViewCount() != requiredTaskViewCount) {
- if (indexOfChild(mClearAllButton) != -1) {
- removeView(mClearAllButton);
- }
-
- for (int i = getTaskViewCount(); i < requiredTaskViewCount; i++) {
- addView(getTaskViewFromPool(false));
- }
- while (getTaskViewCount() > requiredTaskViewCount) {
- removeView(getChildAt(getChildCount() - 1));
- }
- int groupedTaskViewCount = getGroupedTaskViewCount();
- while (requiredGroupTaskViews > groupedTaskViewCount) {
- // Add to front of list
- addView(getTaskViewFromPool(true), 0);
- requiredGroupTaskViews--;
- }
- if (requiredTaskViewCount > 0) {
- addView(mClearAllButton);
- }
- }
-
// Save running task ID if it exists before rebinding all taskViews, otherwise the task from
// the runningTaskView currently bound could get assigned to another TaskView
- // TODO set these type to array and check all taskIDs? Maybe we can get away w/ only one
int runningTaskId = getTaskIdsForTaskViewId(mRunningTaskViewId)[0];
int focusedTaskId = getTaskIdsForTaskViewId(mFocusedTaskViewId)[0];
- Log.d(TASK_VIEW_ID_CRASH, "runningTaskId beforeBind: " + runningTaskId
- + " runningTaskViewId: " + mRunningTaskViewId
- + " forTaskView: " + getTaskViewFromTaskViewId(mRunningTaskViewId));
- for (int taskViewIndex = requiredTaskViewCount - 1, taskDataIndex = taskGroups.size() - 1;
- taskViewIndex >= 0;
- taskViewIndex--, taskDataIndex--) {
- final int pageIndex = requiredTaskViewCount - taskViewIndex - 1;
- // TODO(b/202740477): Temporary assumption, to be updated once groups are actually used
- final Task task = taskGroups.get(taskDataIndex).task1;
- final TaskView taskView = (TaskView) getChildAt(pageIndex);
- if (taskView instanceof GroupedTaskView) {
- Task leftTop;
- Task rightBottom;
- if (task.key.id == splitTaskIds[0]) {
- leftTop = task;
- taskDataIndex--;
- rightBottom = taskGroups.get(taskDataIndex).task1;
- } else {
- rightBottom = task;
- taskDataIndex--;
- leftTop = taskGroups.get(taskDataIndex).task1;
- }
- ((GroupedTaskView) taskView).bind(leftTop, rightBottom, mOrientationState,
- mSplitBoundsConfig);
+ // Removing views sets the currentPage to 0, so we save this and restore it after
+ // the new set of views are added
+ int previousPage = mCurrentPage;
+ removeAllViews();
+
+ // Add views as children based on whether it's grouped or single task
+ for (int i = taskGroups.size() - 1; i >= 0; i--) {
+ GroupTask groupTask = taskGroups.get(i);
+ boolean hasMultipleTasks = groupTask.hasMultipleTasks();
+ TaskView taskView = getTaskViewFromPool(hasMultipleTasks);
+ addView(taskView);
+
+ if (hasMultipleTasks) {
+ ((GroupedTaskView) taskView).bind(groupTask.task1, groupTask.task2,
+ mOrientationState, groupTask.mStagedSplitBounds);
} else {
- taskView.bind(task, mOrientationState);
+ taskView.bind(groupTask.task1, mOrientationState);
}
}
+ if (!taskGroups.isEmpty()) {
+ addView(mClearAllButton);
+ }
+ setCurrentPage(previousPage);
// Keep same previous focused task
TaskView newFocusedTaskView = getTaskViewByTaskId(focusedTaskId);
@@ -1405,21 +1370,10 @@
// Update mRunningTaskViewId to be the new TaskView that was assigned by binding
// the full list of tasks to taskViews
newRunningTaskView = getTaskViewByTaskId(runningTaskId);
- if (newRunningTaskView == null) {
- StringBuilder sb = new StringBuilder();
- for (int i = requiredTaskViewCount - 1; i >= 0; i--) {
- final int pageIndex = requiredTaskViewCount - i - 1;
- final TaskView taskView = (TaskView) getChildAt(pageIndex);
- int taskViewId = taskView.getTaskViewId();
- sb.append(" taskViewId: " + taskViewId
- + " taskId: " + getTaskIdsForTaskViewId(taskViewId)[0]
- + " for taskView: " + taskView + "\n");
- }
- Log.d(TASK_VIEW_ID_CRASH, "taskViewCount: " + getTaskViewCount()
- + " " + sb.toString());
- mRunningTaskViewId = -1;
- } else {
+ if (newRunningTaskView != null) {
mRunningTaskViewId = newRunningTaskView.getTaskViewId();
+ } else {
+ mRunningTaskViewId = -1;
}
}
@@ -1451,6 +1405,7 @@
resetTaskVisuals();
onTaskStackUpdated();
updateEnabledOverlays();
+ mLoadPlanEverApplied = true;
}
private boolean isModal() {
@@ -1919,6 +1874,7 @@
remoteTargetHandle.getTransformParams().setTargetSet(null);
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true);
});
+ mSplitSelectStateController.resetState();
// These are relatively expensive and don't need to be done this frame (RecentsView isn't
// visible anyway), so defer by a frame to get off the critical path, e.g. app to home.
@@ -2768,6 +2724,9 @@
boolean isSplitPlaceholderFirstInGrid = isSplitPlaceholderFirstInGrid();
boolean isSplitPlaceholderLastInGrid = isSplitPlaceholderLastInGrid();
TaskView lastGridTaskView = showAsGrid ? getLastGridTaskView() : null;
+ int currentPageScroll = getScrollForPage(mCurrentPage);
+ int lastGridTaskScroll = getScrollForPage(indexOfChild(lastGridTaskView));
+ boolean currentPageSnapsToEndOfGrid = currentPageScroll == lastGridTaskScroll;
if (lastGridTaskView != null && lastGridTaskView.isVisibleToUser()) {
// After dismissal, animate translation of the remaining tasks to fill any gap left
// between the end of the grid and the clear all button. Only animate if the clear
@@ -2811,8 +2770,7 @@
// Shift all the tasks to make space for split placeholder.
longGridRowWidthDiff += mIsRtl ? mSplitPlaceholderSize : -mSplitPlaceholderSize;
}
- } else if (isLandscapeSplit && getScrollForPage(mCurrentPage)
- == getScrollForPage(indexOfChild(lastGridTaskView))) {
+ } else if (isLandscapeSplit && currentPageSnapsToEndOfGrid) {
// Use last task as reference point for scroll diff and snapping calculation as it's
// the only invariant point in landscape split screen.
snapToLastTask = true;
@@ -3183,14 +3141,39 @@
}
}
+ TaskView newLastGridTaskView = getLastGridTaskView();
if (finalSnapToLastTask) {
// If snapping to last task, find the last task after dismissal.
- pageToSnapTo = indexOfChild(getLastGridTaskView());
+ pageToSnapTo = indexOfChild(newLastGridTaskView);
} else if (taskViewIdToSnapTo != -1) {
// If snapping to another page due to indices rearranging, find
// the new index after dismissal & rearrange using the task view id.
pageToSnapTo = indexOfChild(
getTaskViewFromTaskViewId(taskViewIdToSnapTo));
+ int taskViewToSnapToScroll = getScrollForPage(pageToSnapTo);
+ int lastGridTaskScroll = getScrollForPage(
+ indexOfChild(newLastGridTaskView));
+ if (!currentPageSnapsToEndOfGrid
+ && taskViewToSnapToScroll == lastGridTaskScroll) {
+ // If it wasn't snapped to one of the last pages, but is now
+ // snapped to last pages, we'll need to compensate for the
+ // difference as last pages' scroll is the position where
+ // ClearAllButton is barely invisible, instead of aligned to
+ // mLastComputedTaskSize.
+ int normalTaskEnd = mIsRtl
+ ? mLastComputedTaskSize.right
+ : mLastComputedTaskSize.left;
+ int lastTaskStart = mIsRtl
+ ? mLastComputedGridSize.left
+ : mLastComputedGridSize.right;
+ // As snapped task is not the last task, it can only be the
+ // second last task.
+ int distanceToSnappedTaskEnd =
+ (mPageSpacing + mLastComputedGridTaskSize.width()) * 2;
+ int snappedTaskEnd = lastTaskStart + (mIsRtl
+ ? distanceToSnappedTaskEnd : -distanceToSnappedTaskEnd);
+ mCurrentPageScrollDiff += snappedTaskEnd - normalTaskEnd;
+ }
}
}
setCurrentPage(pageToSnapTo);
@@ -3817,7 +3800,8 @@
* if RecentsView is in portrait or RecentsView isn't shown as grid.
*/
private boolean isSplitPlaceholderFirstInGrid() {
- if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()) {
+ if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()
+ || !isSplitSelectionActive()) {
return false;
}
@StagePosition int position = mSplitSelectStateController.getActiveSplitStagePosition();
@@ -3831,7 +3815,8 @@
* RecentsView is in portrait or RecentsView isn't shown as grid.
*/
private boolean isSplitPlaceholderLastInGrid() {
- if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()) {
+ if (!mActivity.getDeviceProfile().isLandscape || !showAsGrid()
+ || !isSplitSelectionActive()) {
return false;
}
@StagePosition int position = mSplitSelectStateController.getActiveSplitStagePosition();
@@ -4349,6 +4334,7 @@
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) {
mRecentsAnimationController = recentsAnimationController;
+ mSplitSelectStateController.setRecentsAnimationRunning(true);
if (recentsAnimationTargets == null || recentsAnimationTargets.apps.length == 0) {
return;
}
@@ -4448,6 +4434,7 @@
// taps on QSB (3) user goes back to Overview and launch the most recent task.
setCurrentTask(-1);
mRecentsAnimationController = null;
+ mSplitSelectStateController.setRecentsAnimationRunning(false);
executeSideTaskLaunchCallback();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 2ee78dc..8996127 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -557,6 +557,10 @@
return mTaskIdContainer;
}
+ public boolean containsMultipleTasks() {
+ return mTaskIdContainer[1] != -1;
+ }
+
public TaskThumbnailView getThumbnail() {
return mSnapshotView;
}
@@ -689,13 +693,8 @@
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);
opts.options.setLaunchDisplayId(getRootViewDisplayId());
- boolean isOldTaskSplit = LauncherSplitScreenListener.INSTANCE.getNoCreate()
- .getPersistentSplitIds().length > 0;
if (ActivityManagerWrapper.getInstance()
.startActivityFromRecents(mTask.key, opts.options)) {
- if (isOldTaskSplit) {
- SystemUiProxy.INSTANCE.getNoCreate().exitSplitScreen(mTask.key.id);
- }
RecentsView recentsView = getRecentsView();
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && recentsView.getRunningTaskViewId() != -1) {
recentsView.onTaskLaunchedInLiveTileMode();
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
index 6b2d5ed..4e49716 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -30,9 +30,7 @@
import androidx.test.filters.SmallTest;
import com.android.launcher3.util.LooperExecutor;
-import com.android.systemui.shared.recents.model.GroupTask;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.quickstep.util.GroupTask;
import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -73,7 +71,7 @@
@Test
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
- new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo());
+ new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
@@ -93,7 +91,7 @@
ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
task2.taskDescription = new ActivityManager.TaskDescription();
GroupedRecentTaskInfo recentTaskInfos = new GroupedRecentTaskInfo(
- task1, task2);
+ task1, task2, null);
when(mockSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ea65757..93be6fb 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -271,7 +271,9 @@
background.quickSwitchToPreviousAppSwipeLeft();
assertTrue("The 2nd app we should have quick switched to is not running",
isTestActivityRunning(3));
- getAndAssertBackground();
+
+ background = getAndAssertBackground();
+ background.switchToOverview();
}
private boolean isTestActivityRunning(int activityNumber) {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 35c257f..1f1d57a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -141,6 +141,7 @@
private final CheckLongPressHelper mLongPressHelper;
private final boolean mLayoutHorizontal;
+ private final boolean mIsRtl;
private final int mIconSize;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -185,6 +186,8 @@
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
+ mIsRtl = (getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL);
DeviceProfile grid = mActivity.getDeviceProfile();
mDisplay = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
@@ -581,19 +584,29 @@
return mDotInfo != null;
}
+ /**
+ * Get the icon bounds on the view depending on the layout type.
+ */
public void getIconBounds(Rect outBounds) {
- getIconBounds(this, outBounds, mIconSize);
+ getIconBounds(mIconSize, outBounds);
}
- public static void getIconBounds(View iconView, Rect outBounds, int iconSize) {
- int top = iconView.getPaddingTop();
- int left = (iconView.getWidth() - iconSize) / 2;
- int right = left + iconSize;
- int bottom = top + iconSize;
- outBounds.set(left, top, right, bottom);
+ /**
+ * Get the icon bounds on the view depending on the layout type.
+ */
+ public void getIconBounds(int iconSize, Rect outBounds) {
+ Utilities.setRectToViewCenter(this, iconSize, outBounds);
+ if (mLayoutHorizontal) {
+ if (mIsRtl) {
+ outBounds.offsetTo(getWidth() - iconSize - getPaddingRight(), outBounds.top);
+ } else {
+ outBounds.offsetTo(getPaddingLeft(), outBounds.top);
+ }
+ } else {
+ outBounds.offsetTo(outBounds.left, getPaddingTop());
+ }
}
-
/**
* Sets whether to vertically center the content.
*/
@@ -980,8 +993,7 @@
@Override
public void getWorkspaceVisualDragBounds(Rect bounds) {
- DeviceProfile grid = mActivity.getDeviceProfile();
- BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
+ getIconBounds(mIconSize, bounds);
}
private int getIconSizeForDisplay(int display) {
@@ -998,7 +1010,7 @@
}
public void getSourceVisualDragBounds(Rect bounds) {
- BubbleTextView.getIconBounds(this, bounds, getIconSizeForDisplay(mDisplay));
+ getIconBounds(mIconSize, bounds);
}
@Override
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index df09f29..9f3d445 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -483,7 +483,7 @@
LauncherSettings.Favorites.CONTAINER + " FROM "
+ Favorites.TABLE_NAME + ")";
- IntArray folderIds = LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME,
+ IntArray folderIds = LauncherDbUtils.queryIntArray(false, db, Favorites.TABLE_NAME,
Favorites._ID, selection, null, null);
if (!folderIds.isEmpty()) {
db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
@@ -835,8 +835,8 @@
case 27: {
// Update the favorites table so that the screen ids are ordered based on
// workspace page rank.
- IntArray finalScreens = LauncherDbUtils.queryIntArray(db, "workspaceScreens",
- BaseColumns._ID, null, null, "screenRank");
+ IntArray finalScreens = LauncherDbUtils.queryIntArray(false, db,
+ "workspaceScreens", BaseColumns._ID, null, null, "screenRank");
int[] original = finalScreens.toArray();
Arrays.sort(original);
String updatemap = "";
@@ -919,7 +919,7 @@
Log.e(TAG, "getAppWidgetIds not supported", e);
return;
}
- final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(db,
+ final IntSet validWidgets = IntSet.wrap(LauncherDbUtils.queryIntArray(false, db,
Favorites.TABLE_NAME, Favorites.APPWIDGET_ID,
"itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null));
for (int widgetId : allWidgets) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7a38fe7..d2fe483 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -527,6 +527,18 @@
}
/**
+ * Using the view's bounds and icon size, calculate where the icon bounds will
+ * be if it was positioned at the center of the view.
+ */
+ public static void setRectToViewCenter(View iconView, int iconSize, Rect outBounds) {
+ int top = (iconView.getHeight() - iconSize) / 2;
+ int left = (iconView.getWidth() - iconSize) / 2;
+ int right = left + iconSize;
+ int bottom = top + iconSize;
+ outBounds.set(left, top, right, bottom);
+ }
+
+ /**
* Ensures that a value is within given bounds. Specifically:
* If value is less than lowerBound, return lowerBound; else if value is greater than upperBound,
* return upperBound; else return value unchanged.
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 439df80..98be72a 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -628,7 +628,10 @@
public void drawDot(Canvas canvas) {
if (!mForceHideDot && ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0)) {
Rect iconBounds = mDotParams.iconBounds;
- BubbleTextView.getIconBounds(this, iconBounds, mActivity.getDeviceProfile().iconSizePx);
+
+ Utilities.setRectToViewCenter(this, mActivity.getDeviceProfile().iconSizePx,
+ iconBounds);
+ iconBounds.offsetTo(iconBounds.left, getPaddingTop());
float iconScale = (float) mBackground.previewSize / iconBounds.width();
Utilities.scaleRectAboutCenter(iconBounds, iconScale);
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 6855bb1..b510378 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -31,11 +31,11 @@
*/
public class LauncherDbUtils {
- public static IntArray queryIntArray(SQLiteDatabase db, String tableName, String columnName,
- String selection, String groupBy, String orderBy) {
+ public static IntArray queryIntArray(boolean distinct, SQLiteDatabase db, String tableName,
+ String columnName, String selection, String groupBy, String orderBy) {
IntArray out = new IntArray();
- try (Cursor c = db.query(tableName, new String[] { columnName }, selection, null,
- groupBy, null, orderBy)) {
+ try (Cursor c = db.query(distinct, tableName, new String[] { columnName }, selection, null,
+ groupBy, null, orderBy, null)) {
while (c.moveToNext()) {
out.add(c.getInt(0));
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 257d732..8f607a1 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,6 +16,7 @@
package com.android.launcher3.provider;
+import static com.android.launcher3.model.DeviceGridState.TYPE_MULTI_DISPLAY;
import static com.android.launcher3.model.DeviceGridState.TYPE_PHONE;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
@@ -96,7 +97,7 @@
try (SQLiteTransaction t = new SQLiteTransaction(db)) {
RestoreDbTask task = new RestoreDbTask();
task.backupWorkspace(context, db);
- task.sanitizeDB(helper, db, new BackupManager(context));
+ task.sanitizeDB(context, helper, db, new BackupManager(context));
task.restoreAppWidgetIdsIfExists(context);
t.commit();
return true;
@@ -139,7 +140,7 @@
GridBackupTable backupTable = new GridBackupTable(context, db, idp.numDatabaseHotseatIcons,
idp.numColumns, idp.numRows);
if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
- int itemsDeleted = sanitizeDB(helper, db, backupManager);
+ int itemsDeleted = sanitizeDB(context, helper, db, backupManager);
LauncherAppState.getInstance(context).getModel().forceReload();
restoreAppWidgetIdsIfExists(context);
if (itemsDeleted == 0) {
@@ -156,11 +157,12 @@
* the restored apps get installed.
* 3. If the user serial for any restored profile is different than that of the previous
* device, update the entries to the new profile id.
+ * 4. If restored from a single display backup, remove gaps between screenIds
*
* @return number of items deleted.
*/
- private int sanitizeDB(DatabaseHelper helper, SQLiteDatabase db, BackupManager backupManager)
- throws Exception {
+ private int sanitizeDB(Context context, DatabaseHelper helper, SQLiteDatabase db,
+ BackupManager backupManager) throws Exception {
// Primary user ids
long myProfileId = helper.getDefaultUserSerial();
long oldProfileId = getDefaultProfileId(db);
@@ -236,10 +238,43 @@
if (myProfileId != oldProfileId) {
changeDefaultColumn(db, myProfileId);
}
+
+ // If restored from a single display backup, remove gaps between screenIds
+ if (Utilities.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE)
+ != TYPE_MULTI_DISPLAY) {
+ removeScreenIdGaps(db);
+ }
+
return itemsDeleted;
}
/**
+ * Remove gaps between screenIds to make sure no empty pages are left in between.
+ *
+ * e.g. [0, 3, 4, 6, 7] -> [0, 1, 2, 3, 4]
+ */
+ protected void removeScreenIdGaps(SQLiteDatabase db) {
+ FileLog.d(TAG, "Removing gaps between screenIds");
+ IntArray distinctScreens = LauncherDbUtils.queryIntArray(true, db, Favorites.TABLE_NAME,
+ Favorites.SCREEN, Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP, null,
+ Favorites.SCREEN);
+ if (distinctScreens.isEmpty()) {
+ return;
+ }
+
+ StringBuilder sql = new StringBuilder("UPDATE ").append(Favorites.TABLE_NAME)
+ .append(" SET ").append(Favorites.SCREEN).append(" =\nCASE\n");
+ int screenId = distinctScreens.contains(0) ? 0 : 1;
+ for (int i = 0; i < distinctScreens.size(); i++) {
+ sql.append("WHEN ").append(Favorites.SCREEN).append(" == ")
+ .append(distinctScreens.get(i)).append(" THEN ").append(screenId++).append("\n");
+ }
+ sql.append("ELSE screen\nEND WHERE ").append(Favorites.CONTAINER).append(" = ")
+ .append(Favorites.CONTAINER_DESKTOP).append(";");
+ db.execSQL(sql.toString());
+ }
+
+ /**
* Updates profile id of all entries from {@param oldProfileId} to {@param newProfileId}.
*/
protected void migrateProfileId(SQLiteDatabase db, long oldProfileId, long newProfileId) {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index a190f52..93e3ea7 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -390,7 +390,7 @@
@Override
public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
int parentWidth, int parentHeight,
- SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
+ StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
int dividerBar = splitBoundsConfig.visualDividerBounds.width();
@@ -435,15 +435,18 @@
(FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
FrameLayout.LayoutParams secondaryIconParams =
new FrameLayout.LayoutParams(primaryIconParams);
+ int dividerBar = (splitConfig.appsStackedVertically ?
+ splitConfig.visualDividerBounds.height() :
+ splitConfig.visualDividerBounds.width());
int primaryHeight = primarySnapshotBounds.height();
- int secondaryHeight = secondarySnapshotBounds.height();
primaryIconParams.gravity = (isRtl ? START : END) | TOP;
- primaryIconView.setTranslationY((primaryHeight + taskIconHeight) / 2f );
+ primaryIconView.setTranslationY(primaryHeight - primaryIconView.getHeight() / 2f);
+ primaryIconView.setTranslationX(0);
secondaryIconParams.gravity = (isRtl ? START : END) | TOP;
- secondaryIconView.setTranslationY(primaryHeight
- + ((secondaryHeight + taskIconHeight) / 2f));
+ secondaryIconView.setTranslationY(primaryHeight + taskIconHeight + dividerBar);
+ secondaryIconView.setTranslationX(0);
primaryIconView.setLayoutParams(primaryIconParams);
secondaryIconView.setLayoutParams(secondaryIconParams);
}
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 8112afd..2ff2feb 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -141,13 +141,12 @@
* @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize
* outRect for
*/
- void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
- StagedSplitBounds splitInfo,
+ void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, StagedSplitBounds splitInfo,
@SplitConfigurationOptions.StagePosition int desiredStagePosition);
void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
int parentWidth, int parentHeight,
- SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp);
+ StagedSplitBounds splitBoundsConfig, DeviceProfile dp);
// Overview TaskMenuView methods
void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 576c6f5..8caf886 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -16,7 +16,6 @@
package com.android.launcher3.touch;
-import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
@@ -477,7 +476,7 @@
@Override
public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
int parentWidth, int parentHeight,
- SplitConfigurationOptions.StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
+ StagedSplitBounds splitBoundsConfig, DeviceProfile dp) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
int dividerBar = (splitBoundsConfig.appsStackedVertically ?
@@ -535,23 +534,27 @@
(FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
FrameLayout.LayoutParams secondaryIconParams =
new FrameLayout.LayoutParams(primaryIconParams);
+ int dividerBar = (splitConfig.appsStackedVertically ?
+ splitConfig.visualDividerBounds.height() :
+ splitConfig.visualDividerBounds.width());
+ int primaryWidth = primarySnapshotBounds.width();
if (deviceProfile.isLandscape) {
- int primaryWidth = primarySnapshotBounds.width();
- int secondaryWidth = secondarySnapshotBounds.width();
primaryIconParams.gravity = TOP | START;
- primaryIconView.setTranslationX((primaryWidth - taskIconHeight) / 2f );
+ primaryIconView.setTranslationX(primaryWidth - primaryIconView.getWidth());
+ primaryIconView.setTranslationY(0);
secondaryIconParams.gravity = TOP | START;
- secondaryIconView.setTranslationX(primaryWidth
- + ((secondaryWidth - taskIconHeight) / 2f));
- } else {
- primaryIconView.setTranslationX(0);
- secondaryIconView.setTranslationX(0);
- primaryIconView.setTranslationY(0);
+ secondaryIconView.setTranslationX(primaryWidth + dividerBar);
secondaryIconView.setTranslationY(0);
- secondaryIconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
- secondaryIconParams.bottomMargin = -(secondaryIconParams.topMargin + taskIconHeight);
+ } else {
+ primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
+ primaryIconView.setTranslationX(-(primaryIconView.getWidth()) / 2f);
+ primaryIconView.setTranslationY(0);
+
+ secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
+ secondaryIconView.setTranslationX(secondaryIconView.getWidth() / 2f);
+ secondaryIconView.setTranslationY(0);
}
primaryIconView.setLayoutParams(primaryIconParams);
secondaryIconView.setLayoutParams(secondaryIconParams);
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index d5851c8..a0dde22 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -19,6 +19,7 @@
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
import static android.view.Gravity.START;
+import static android.view.Gravity.TOP;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -36,7 +37,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
import com.android.launcher3.views.BaseDragLayer;
import java.util.Collections;
@@ -127,6 +130,23 @@
iconParams.topMargin = snapshotParams.topMargin / 2;
}
+ @Override
+ public void setSplitIconParams(View primaryIconView, View secondaryIconView,
+ int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+ boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
+ super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight,
+ primarySnapshotBounds, secondarySnapshotBounds, isRtl, deviceProfile, splitConfig);
+ FrameLayout.LayoutParams primaryIconParams =
+ (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
+ FrameLayout.LayoutParams secondaryIconParams =
+ (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams();
+
+ primaryIconParams.gravity = (isRtl ? END : START) | TOP;
+ secondaryIconParams.gravity = (isRtl ? END : START) | TOP;
+ primaryIconView.setLayoutParams(primaryIconParams);
+ secondaryIconView.setLayoutParams(secondaryIconParams);
+ }
+
/* ---------- 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
index 0b083e3..6aef38f 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -85,6 +85,12 @@
}
}
+ /**
+ * NOTE: Engineers complained about too little ambiguity in the last survey, so there is a class
+ * with the same name/functionality in wm.shell.util (which launcher3 cannot be built against)
+ *
+ * If you make changes here, consider making the same changes there
+ */
public static class StagedSplitBounds {
public final Rect leftTopBounds;
public final Rect rightBottomBounds;
@@ -100,10 +106,15 @@
* the bounds were originally in
*/
public final boolean appsStackedVertically;
+ public final int leftTopTaskId;
+ public final int rightBottomTaskId;
- public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds) {
+ public StagedSplitBounds(Rect leftTopBounds, Rect rightBottomBounds, int leftTopTaskId,
+ int rightBottomTaskId) {
this.leftTopBounds = leftTopBounds;
this.rightBottomBounds = rightBottomBounds;
+ this.leftTopTaskId = leftTopTaskId;
+ this.rightBottomTaskId = rightBottomTaskId;
if (rightBottomBounds.top > leftTopBounds.top) {
// vertical apps, horizontal divider
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 48305ee..9c8de1c 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.provider;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import android.content.ContentValues;
@@ -87,6 +88,56 @@
assertEquals(1, getCount(db, "select * from favorites where profileId = 33"));
}
+ @Test
+ public void testRemoveScreenIdGaps_firstScreenEmpty() {
+ runRemoveScreenIdGapsTest(
+ new int[]{1, 2, 5, 6, 6, 7, 9, 9},
+ new int[]{1, 2, 3, 4, 4, 5, 6, 6});
+ }
+
+ @Test
+ public void testRemoveScreenIdGaps_firstScreenOccupied() {
+ runRemoveScreenIdGapsTest(
+ new int[]{0, 2, 5, 6, 6, 7, 9, 9},
+ new int[]{0, 1, 2, 3, 3, 4, 5, 5});
+ }
+
+ @Test
+ public void testRemoveScreenIdGaps_noGap() {
+ runRemoveScreenIdGapsTest(
+ new int[]{0, 1, 1, 2, 3, 3, 4, 5},
+ new int[]{0, 1, 1, 2, 3, 3, 4, 5});
+ }
+
+ private void runRemoveScreenIdGapsTest(int[] screenIds, int[] expectedScreenIds) {
+ SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+ // Add some mock data
+ for (int i = 0; i < screenIds.length; i++) {
+ ContentValues values = new ContentValues();
+ values.put(Favorites._ID, i);
+ values.put(Favorites.SCREEN, screenIds[i]);
+ values.put(Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP);
+ db.insert(Favorites.TABLE_NAME, null, values);
+ }
+ // Verify items are added
+ assertEquals(screenIds.length,
+ getCount(db, "select * from favorites where container = -100"));
+
+ new RestoreDbTask().removeScreenIdGaps(db);
+
+ // verify screenId gaps removed
+ int[] resultScreenIds = new int[screenIds.length];
+ try (Cursor c = db.rawQuery(
+ "select screen from favorites where container = -100 order by screen", null)) {
+ int i = 0;
+ while (c.moveToNext()) {
+ resultScreenIds[i++] = c.getInt(0);
+ }
+ }
+
+ assertArrayEquals(expectedScreenIds, resultScreenIds);
+ }
+
private int getCount(SQLiteDatabase db, String sql) {
try (Cursor c = db.rawQuery(sql, null)) {
return c.getCount();
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index aca5951..45d20e2 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -59,6 +59,15 @@
String[] tokens = output.split("\\s+");
mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
mDevice.executeShellCommand("am start-user " + mProfileUserId);
+
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+ waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal",
+ () -> NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+ executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+ waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps",
+ () -> ALL_APPS);
}
@After
@@ -89,13 +98,6 @@
@Test
@ScreenRecord // b/202735477
public void workTabExists() {
- mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
- waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal",
- () -> NORMAL);
- executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
- waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps",
- () -> ALL_APPS);
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible(),
LauncherInstrumentation.WAIT_TIME_MS);
@@ -106,12 +108,6 @@
@Test
public void toggleWorks() {
- mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
- waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
- executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
- waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
-
waitForWorkTabSetup();
executeOnLauncher(launcher -> {
@@ -153,11 +149,6 @@
@Test
public void testEdu() {
- mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
- waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
- executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
- waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
waitForWorkTabSetup();
executeOnLauncher(l -> {
l.getSharedPrefs().edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, 0).commit();
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index b290bb1..4eaecca 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -30,6 +30,7 @@
import com.android.launcher3.testing.TestProtocol;
+import java.util.List;
import java.util.regex.Pattern;
/**
@@ -62,11 +63,12 @@
"want to switch from background to overview")) {
verifyActiveContainer();
goToOverviewUnchecked();
- return mLauncher.isFallbackOverview() ?
- new BaseOverview(mLauncher) : new Overview(mLauncher);
+ return mLauncher.isFallbackOverview()
+ ? new BaseOverview(mLauncher) : new Overview(mLauncher);
}
}
+
protected boolean zeroButtonToOverviewGestureStartsInLauncher() {
return mLauncher.isTablet();
}
@@ -78,47 +80,56 @@
protected void goToOverviewUnchecked() {
switch (mLauncher.getNavigationModel()) {
case ZERO_BUTTON: {
- final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
- final int startY = getSwipeStartY();
- final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- final Point start = new Point(centerX, startY);
- final Point end =
- new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
-
- final long downTime = SystemClock.uptimeMillis();
- final LauncherInstrumentation.GestureScope gestureScope =
- zeroButtonToOverviewGestureStartsInLauncher()
- ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
- : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
-
- mLauncher.sendPointer(
- downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
- Runnable swipeAndHold = () -> mLauncher.movePointer(
- downTime,
- downTime,
- ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
- start,
- end,
- gestureScope);
- String swipeAndHoldAction = "swiping and holding";
- Runnable up = () -> mLauncher.sendPointer(
- downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end,
- gestureScope);
- String upAction = "sending UP event";
+ sendDownPointerToEnterOverviewToLauncher();
+ String swipeAndHoldToEnterOverviewActionName =
+ "swiping and holding to enter overview";
+ // If swiping from an app (e.g. Overview is in Background), we pause and hold on
+ // swipe up to make overview appear, or else swiping without holding would take
+ // us to the Home state. If swiping up from Home (e.g. Overview in Home or
+ // Workspace state where the below condition is true), there is no need to pause,
+ // and we will not test for an intermediate carousel as one will not exist.
if (zeroButtonToOverviewGestureStateTransitionWhileHolding()) {
- mLauncher.runToState(swipeAndHold, OVERVIEW_STATE_ORDINAL, swipeAndHoldAction);
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(upAction)) {
- up.run();
- }
+ mLauncher.runToState(this::sendSwipeUpAndHoldToEnterOverviewGestureToLauncher,
+ OVERVIEW_STATE_ORDINAL, swipeAndHoldToEnterOverviewActionName);
+ sendUpPointerToEnterOverviewToLauncher();
} else {
+ // If swiping up from an app to overview, pause on intermediate carousel
+ // until snapshots are visible. No intermediate carousel when swiping from
+ // Home. The task swiped up is not a snapshot but the TaskViewSimulator. If
+ // only a single task exists, no snapshots will be available during swipe up.
mLauncher.executeAndWaitForLauncherEvent(
- swipeAndHold,
+ this::sendSwipeUpAndHoldToEnterOverviewGestureToLauncher,
event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(
- event.getClassName()),
+ event.getClassName().toString()),
() -> "Pause wasn't detected",
- swipeAndHoldAction);
- mLauncher.runToState(up, OVERVIEW_STATE_ORDINAL, upAction);
+ swipeAndHoldToEnterOverviewActionName);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "paused on swipe up to overview")) {
+ if (mLauncher.getRecentTasks().size() > 1) {
+ // When swiping up to grid-overview for tablets, the swiped tab will be
+ // in the middle of the screen (TaskViewSimulator, not a snapshot), and
+ // all remaining snapshots will be to the left of that task. In
+ // non-tablet overview, snapshots can be on either side of the swiped
+ // task, but we still check that they become visible after swiping and
+ // pausing.
+ mLauncher.waitForOverviewObject("snapshot");
+ if (mLauncher.isTablet()) {
+ List<UiObject2> tasks = mLauncher.getDevice().findObjects(
+ mLauncher.getOverviewObjectSelector("snapshot"));
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ mLauncher.assertTrue(
+ "All tasks not to the left of the swiped task",
+ tasks.stream()
+ .allMatch(
+ t -> t.getVisibleBounds().right < centerX));
+ }
+
+ }
+ String upPointerToEnterOverviewActionName =
+ "sending UP pointer to enter overview";
+ mLauncher.runToState(this::sendUpPointerToEnterOverviewToLauncher,
+ OVERVIEW_STATE_ORDINAL, upPointerToEnterOverviewActionName);
+ }
}
break;
}
@@ -167,6 +178,60 @@
private void expectSwitchToOverviewEvents() {
}
+ private void sendDownPointerToEnterOverviewToLauncher() {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final Point start = new Point(centerX, startY);
+ final long downTime = SystemClock.uptimeMillis();
+ final LauncherInstrumentation.GestureScope gestureScope =
+ zeroButtonToOverviewGestureStartsInLauncher()
+ ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+ : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+
+ mLauncher.sendPointer(
+ downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
+ }
+
+ private void sendSwipeUpAndHoldToEnterOverviewGestureToLauncher() {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final Point start = new Point(centerX, startY);
+ final Point end =
+ new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
+ final long downTime = SystemClock.uptimeMillis();
+ final LauncherInstrumentation.GestureScope gestureScope =
+ zeroButtonToOverviewGestureStartsInLauncher()
+ ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+ : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+
+ mLauncher.movePointer(
+ downTime,
+ downTime,
+ ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
+ start,
+ end,
+ gestureScope);
+ }
+
+ private void sendUpPointerToEnterOverviewToLauncher() {
+ final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
+ final int startY = getSwipeStartY();
+ final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).getInt(
+ TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final Point end =
+ new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
+ final long downTime = SystemClock.uptimeMillis();
+ final LauncherInstrumentation.GestureScope gestureScope =
+ zeroButtonToOverviewGestureStartsInLauncher()
+ ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+ : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+
+ mLauncher.sendPointer(downTime, SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_UP, end, gestureScope);
+ }
+
@NonNull
public Background quickSwitchToPreviousApp() {
boolean toRight = true;
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5a807ca..f3e3ec5 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1156,6 +1156,11 @@
}
@NonNull
+ UiObject2 waitForOverviewObject(String resName) {
+ return waitForObjectBySelector(getOverviewObjectSelector(resName));
+ }
+
+ @NonNull
UiObject2 waitForLauncherObject(String resName) {
return waitForObjectBySelector(getLauncherObjectSelector(resName));
}