Merge "Add Utility method in userCache to get badgeDrawables" into main
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index b846604..4e16e7f 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -34,3 +34,10 @@
description: "This flag disables accessibility drag for Private Space Apps."
bug: "289223923"
}
+
+flag {
+ name: "private_space_restrict_item_drag"
+ namespace: "launcher_search"
+ description: "This flag disables drag and drop for Private Space Items."
+ bug: "289223923"
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 5caf004..f15d12b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -22,6 +22,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -47,7 +48,8 @@
public final class KeyboardQuickSwitchController implements
TaskbarControllers.LoggableTaskbarController {
- static final int MAX_TASKS = 6;
+ @VisibleForTesting
+ public static final int MAX_TASKS = 6;
@NonNull private final ControllerCallbacks mControllerCallbacks = new ControllerCallbacks();
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index ac47c60..fe91362 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -48,7 +48,7 @@
protected val imeSwitcher: ImageView?,
protected val rotationButton: RotationButton?,
protected val a11yButton: ImageView?,
- protected val space: Space
+ protected val space: Space?
) : NavButtonLayoutter {
protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home)
protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps)
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
index 7578b4c..4368b95 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -39,7 +39,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space,
+ space: Space?
) :
AbstractNavButtonLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 1d4ae7b..2b60dc0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -61,7 +61,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space,
+ space: Space?,
resources: Resources,
isKidsMode: Boolean,
isInSetup: Boolean,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index 6d86f1b..bf820c0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -33,7 +33,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space,
+ space: Space?
) :
AbstractNavButtonLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index db15542..6a935f1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -36,7 +36,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space,
+ space: Space?
) :
AbstractNavButtonLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index 3eb25a2..0672270 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -36,7 +36,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space
+ space: Space?
) :
AbstractNavButtonLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
index 899aab4..869cc43 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
@@ -34,7 +34,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space,
+ space: Space?
) :
PhoneLandscapeNavLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index f31443c..181e0ed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -36,7 +36,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space,
+ space: Space?
) :
AbstractNavButtonLayoutter(
resources,
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 69e524e..5c57a01 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -39,7 +39,7 @@
imeSwitcher: ImageView?,
rotationButton: RotationButton?,
a11yButton: ImageView?,
- space: Space
+ space: Space?
) :
AbstractNavButtonLayoutter(
resources,
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 60d0e2b..6698600 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -22,7 +22,9 @@
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.DECELERATE;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.BaseActivity.EVENT_STARTED;
@@ -134,6 +136,7 @@
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator;
import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
@@ -167,6 +170,9 @@
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
+ // Fraction of the scroll and transform animation in which the current task fades out
+ private static final float KQS_TASK_FADE_ANIMATION_FRACTION = 0.4f;
+
protected final BaseActivityInterface<S, T> mActivityInterface;
protected final InputConsumerProxy mInputConsumerProxy;
protected final ActivityInitListener mActivityInitListener;
@@ -900,7 +906,10 @@
return;
}
mLauncherTransitionController.setProgress(
- Math.max(mCurrentShift.value, getScaleProgressDueToScroll()), mDragLengthFactor);
+ // Immediately finish the grid transition
+ isKeyboardTaskFocusPending()
+ ? 1f : Math.max(mCurrentShift.value, getScaleProgressDueToScroll()),
+ mDragLengthFactor);
}
/**
@@ -1349,7 +1358,9 @@
}
Interpolator interpolator;
S state = mActivityInterface.stateFromGestureEndTarget(endTarget);
- if (state.displayOverviewTasksAsGrid(mDp)) {
+ if (isKeyboardTaskFocusPending()) {
+ interpolator = EMPHASIZED;
+ } else if (state.displayOverviewTasksAsGrid(mDp)) {
interpolator = ACCELERATE_DECELERATE;
} else if (endTarget == RECENTS) {
interpolator = OVERSHOOT_1_2;
@@ -1653,7 +1664,8 @@
animatorSet.play(windowAnim);
if (mRecentsView != null) {
mRecentsView.onPrepareGestureEndAnimation(
- animatorSet, mGestureState.getEndTarget(),
+ mGestureState.isHandlingAtomicEvent() ? null : animatorSet,
+ mGestureState.getEndTarget(),
getRemoteTaskViewSimulators());
}
animatorSet.setDuration(duration).setInterpolator(interpolator);
@@ -2225,6 +2237,14 @@
}
}
+ private boolean shouldLinkRecentsViewScroll() {
+ return mRecentsViewScrollLinked && !isKeyboardTaskFocusPending();
+ }
+
+ private boolean isKeyboardTaskFocusPending() {
+ return mRecentsView != null && mRecentsView.isKeyboardTaskFocusPending();
+ }
+
private void onRecentsViewScroll() {
if (moveWindowWithRecentsScroll()) {
onCurrentShiftUpdated();
@@ -2457,6 +2477,44 @@
mActivityInitListener.register();
}
+ private boolean shouldFadeOutTargetsForKeyboardQuickSwitch(
+ TransformParams transformParams,
+ TaskViewSimulator taskViewSimulator,
+ float progress) {
+ RemoteAnimationTargets targets = transformParams.getTargetSet();
+ boolean fadeAppTargets = isKeyboardTaskFocusPending()
+ && targets != null
+ && targets.apps != null
+ && targets.apps.length > 0;
+ float fadeProgress = Utilities.mapBoundToRange(
+ progress,
+ /* lowerBound= */ 0f,
+ /* upperBound= */ KQS_TASK_FADE_ANIMATION_FRACTION,
+ /* toMin= */ 0f,
+ /* toMax= */ 1f,
+ LINEAR);
+ if (!fadeAppTargets || Float.compare(fadeProgress, 1f) == 0) {
+ return false;
+ }
+ SurfaceTransaction surfaceTransaction =
+ transformParams.createSurfaceParams(taskViewSimulator);
+ SurfaceControl.Transaction transaction = surfaceTransaction.getTransaction();
+
+ for (RemoteAnimationTarget app : targets.apps) {
+ transaction.setAlpha(app.leash, 1f - fadeProgress);
+ transaction.setPosition(app.leash,
+ /* x= */ app.startBounds.left
+ + (mActivity.getDeviceProfile().overviewPageSpacing
+ * (mRecentsView.isRtl() ? fadeProgress : -fadeProgress)),
+ /* y= */ 0f);
+ transaction.setScale(app.leash, 1f, 1f);
+ taskViewSimulator.taskPrimaryTranslation.value =
+ mRecentsView.getScrollOffsetForKeyboardTaskFocus();
+ taskViewSimulator.apply(transformParams, surfaceTransaction);
+ }
+ return true;
+ }
+
/**
* Applies the transform on the recents animation
*/
@@ -2466,7 +2524,7 @@
// swipe-to-icon animation is handled by RectFSpringAnim anim
boolean notSwipingToHome = mRecentsAnimationTargets != null
&& mGestureState.getEndTarget() != HOME;
- boolean setRecentsScroll = mRecentsViewScrollLinked && mRecentsView != null;
+ boolean setRecentsScroll = shouldLinkRecentsViewScroll() && mRecentsView != null;
float progress = Math.max(mCurrentShift.value, getScaleProgressDueToScroll());
int scrollOffset = setRecentsScroll ? mRecentsView.getScrollOffset() : 0;
if (!mStartMovingTasks && (progress > 0 || scrollOffset != 0)) {
@@ -2485,7 +2543,12 @@
if (setRecentsScroll) {
taskViewSimulator.setScroll(scrollOffset);
}
- taskViewSimulator.apply(remoteHandle.getTransformParams());
+ TransformParams transformParams = remoteHandle.getTransformParams();
+ if (shouldFadeOutTargetsForKeyboardQuickSwitch(
+ transformParams, taskViewSimulator, progress)) {
+ continue;
+ }
+ taskViewSimulator.apply(transformParams);
}
}
}
@@ -2493,7 +2556,7 @@
// Scaling of RecentsView during quick switch based on amount of recents scroll
private float getScaleProgressDueToScroll() {
if (mActivity == null || !mActivity.getDeviceProfile().isTablet || mRecentsView == null
- || !mRecentsViewScrollLinked) {
+ || !shouldLinkRecentsViewScroll()) {
return 0;
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index ca82a2d..b89d20c 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -178,7 +178,7 @@
public abstract <T extends RecentsView> T getVisibleRecentsView();
@UiThread
- public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
+ public abstract boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener);
public abstract Rect getOverviewWindowBounds(
Rect homeBounds, RemoteAnimationTarget target);
@@ -520,7 +520,7 @@
// Since we are changing the start position of the UI, reapply the state, at the end
controller.setEndAction(() -> mActivity.getStateManager().goToState(
controller.getInterpolatedProgress() > 0.5 ? mTargetState : mBackgroundState,
- false));
+ /* animated= */ false));
RecentsView recentsView = mActivity.getOverviewPanel();
AnimatorControllerWithResistance controllerWithResistance =
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 693d6ae..27e8726 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -122,7 +122,7 @@
}
@Override
- public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+ public boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener) {
return false;
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 70b9183..97c48e6 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherState.FLOATING_SEARCH_BAR;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
@@ -212,7 +211,7 @@
}
@Override
- public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+ public boolean switchToRecentsIfVisible(Animator.AnimatorListener animatorListener) {
Launcher launcher = getVisibleLauncher();
if (launcher == null) {
return false;
@@ -227,7 +226,7 @@
closeOverlay();
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(),
- onCompleteCallback == null ? null : forEndCallback(onCompleteCallback));
+ animatorListener);
return true;
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 315316d..e448a14 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -15,9 +15,12 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.PagedView.INVALID_PAGE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.content.Intent;
import android.graphics.PointF;
import android.os.SystemClock;
@@ -25,7 +28,6 @@
import android.view.View;
import androidx.annotation.BinderThread;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -75,7 +77,7 @@
* do not lose the focus across multiple calls of
* {@link OverviewCommandHelper#executeCommand(CommandInfo)} for the same command
*/
- private int mTaskFocusIndexOverride = -1;
+ private int mKeyboardTaskFocusIndex = -1;
/**
* Whether we should incoming toggle commands while a previous toggle command is still ongoing.
@@ -195,9 +197,11 @@
}
BaseActivityInterface<?, T> activityInterface =
mOverviewComponentObserver.getActivityInterface();
- RecentsView recents = activityInterface.getVisibleRecentsView();
- if (recents == null) {
+ RecentsView visibleRecentsView = activityInterface.getVisibleRecentsView();
+ RecentsView createdRecentsView;
+ if (visibleRecentsView == null) {
T activity = activityInterface.getCreatedActivity();
+ createdRecentsView = activity == null ? null : activity.getOverviewPanel();
DeviceProfile dp = activity == null ? null : activity.getDeviceProfile();
TaskbarUIController uiController = activityInterface.getTaskbarController();
boolean allowQuickSwitch = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
@@ -209,8 +213,8 @@
if (!allowQuickSwitch) {
return true;
}
- mTaskFocusIndexOverride = uiController.launchFocusedTask();
- if (mTaskFocusIndexOverride == -1) {
+ mKeyboardTaskFocusIndex = uiController.launchFocusedTask();
+ if (mKeyboardTaskFocusIndex == -1) {
return true;
}
}
@@ -224,34 +228,47 @@
return true;
}
} else {
+ createdRecentsView = visibleRecentsView;
switch (cmd.type) {
case TYPE_SHOW:
// already visible
return true;
case TYPE_HIDE: {
- mTaskFocusIndexOverride = -1;
- int currentPage = recents.getNextPage();
- TaskView tv = (currentPage >= 0 && currentPage < recents.getTaskViewCount())
- ? (TaskView) recents.getPageAt(currentPage)
+ mKeyboardTaskFocusIndex = INVALID_PAGE;
+ int currentPage = visibleRecentsView.getNextPage();
+ TaskView tv = (currentPage >= 0
+ && currentPage < visibleRecentsView.getTaskViewCount())
+ ? (TaskView) visibleRecentsView.getPageAt(currentPage)
: null;
- return launchTask(recents, tv, cmd);
+ return launchTask(visibleRecentsView, tv, cmd);
}
case TYPE_TOGGLE:
- return launchTask(recents, getNextTask(recents), cmd);
+ return launchTask(visibleRecentsView, getNextTask(visibleRecentsView), cmd);
case TYPE_HOME:
- recents.startHome();
+ visibleRecentsView.startHome();
return true;
}
}
- final Runnable completeCallback = () -> {
- RecentsView rv = activityInterface.getVisibleRecentsView();
- if (rv != null && (cmd.type == TYPE_KEYBOARD_INPUT || cmd.type == TYPE_HIDE)) {
- updateRecentsViewFocus(rv);
+ if (createdRecentsView != null) {
+ createdRecentsView.setKeyboardTaskFocusIndex(mKeyboardTaskFocusIndex);
+ }
+ // Handle recents view focus when launching from home
+ Animator.AnimatorListener animatorListener = new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ updateRecentsViewFocus(cmd);
}
- scheduleNextTask(cmd);
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ onRecentsViewFocusUpdated(cmd);
+ scheduleNextTask(cmd);
+ }
};
- if (activityInterface.switchToRecentsIfVisible(completeCallback)) {
+ if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
// If successfully switched, wait until animation finishes
return false;
}
@@ -276,6 +293,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
+ updateRecentsViewFocus(cmd);
activityInterface.runOnInitBackgroundStateUI(() ->
interactionHandler.onGestureEnded(0, new PointF()));
cmd.removeListener(this);
@@ -290,14 +308,12 @@
if (createdActivity == null) {
return;
}
- RecentsView createdRecents = createdActivity.getOverviewPanel();
- if (createdRecents != null) {
- createdRecents.onRecentsAnimationComplete();
+ if (createdRecentsView != null) {
+ createdRecentsView.onRecentsAnimationComplete();
}
}
};
- RecentsView<?, ?> visibleRecentsView = activityInterface.getVisibleRecentsView();
if (visibleRecentsView != null) {
visibleRecentsView.moveRunningTaskToFront();
}
@@ -317,7 +333,6 @@
interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
cmd.mActiveCallbacks.addListener(recentAnimListener);
}
-
Trace.beginAsyncSection(TRANSITION_NAME, 0);
return false;
}
@@ -325,47 +340,58 @@
private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
cmd.removeListener(handler);
Trace.endAsyncSection(TRANSITION_NAME, 0);
-
- RecentsView rv =
- mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
- if (rv != null && (cmd.type == TYPE_KEYBOARD_INPUT || cmd.type == TYPE_HIDE)) {
- updateRecentsViewFocus(rv);
- }
+ onRecentsViewFocusUpdated(cmd);
scheduleNextTask(cmd);
}
- private void updateRecentsViewFocus(@NonNull RecentsView rv) {
+ private void updateRecentsViewFocus(CommandInfo cmd) {
+ RecentsView recentsView =
+ mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
+ if (recentsView == null || (cmd.type != TYPE_KEYBOARD_INPUT && cmd.type != TYPE_HIDE)) {
+ return;
+ }
// When the overview is launched via alt tab (cmd type is TYPE_KEYBOARD_INPUT),
// the touch mode somehow is not change to false by the Android framework.
// The subsequent tab to go through tasks in overview can only be dispatched to
// focuses views, while focus can only be requested in
// {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To note,
// here we launch overview with live tile.
- rv.getViewRootImpl().touchModeChanged(false);
+ recentsView.getViewRootImpl().touchModeChanged(false);
// Ensure that recents view has focus so that it receives the followup key inputs
- TaskView taskView = rv.getTaskViewAt(mTaskFocusIndexOverride);
- if (taskView != null) {
- requestFocus(taskView);
+ if (requestFocus(recentsView.getTaskViewAt(mKeyboardTaskFocusIndex))) {
return;
}
- taskView = rv.getNextTaskView();
- if (taskView != null) {
- requestFocus(taskView);
+ if (requestFocus(recentsView.getNextTaskView())) {
return;
}
- taskView = rv.getTaskViewAt(0);
- if (taskView != null) {
- requestFocus(taskView);
+ if (requestFocus(recentsView.getTaskViewAt(0))) {
return;
}
- requestFocus(rv);
+ requestFocus(recentsView);
}
- private void requestFocus(@NonNull View view) {
- view.post(() -> {
- view.requestFocus();
- view.requestAccessibilityFocus();
+ private void onRecentsViewFocusUpdated(CommandInfo cmd) {
+ RecentsView recentsView =
+ mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
+ if (recentsView == null
+ || cmd.type != TYPE_HIDE
+ || mKeyboardTaskFocusIndex == INVALID_PAGE) {
+ return;
+ }
+ recentsView.setKeyboardTaskFocusIndex(INVALID_PAGE);
+ recentsView.setCurrentPage(mKeyboardTaskFocusIndex);
+ mKeyboardTaskFocusIndex = INVALID_PAGE;
+ }
+
+ private boolean requestFocus(@Nullable View taskView) {
+ if (taskView == null) {
+ return false;
+ }
+ taskView.post(() -> {
+ taskView.requestFocus();
+ taskView.requestAccessibilityFocus();
});
+ return true;
}
public void dump(PrintWriter pw) {
@@ -374,7 +400,7 @@
if (!mPendingCommands.isEmpty()) {
pw.println(" pendingCommandType=" + mPendingCommands.get(0).type);
}
- pw.println(" mTaskFocusIndexOverride=" + mTaskFocusIndexOverride);
+ pw.println(" mKeyboardTaskFocusIndex=" + mKeyboardTaskFocusIndex);
pw.println(" mWaitForToggleCommandComplete=" + mWaitForToggleCommandComplete);
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 4f885af..a1a3145 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -66,6 +66,8 @@
private Runnable mLiveTileCleanUpHandler;
private Context mCtx;
+ private boolean mRecentsAnimationStartPending = false;
+
private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
@@ -100,6 +102,10 @@
.startRecentsActivity(intent, 0, null, null, null));
}
+ public boolean isRecentsAnimationStartPending() {
+ return mRecentsAnimationStartPending;
+ }
+
/**
* Starts a new recents animation for the activity with the given {@param intent}.
*/
@@ -109,6 +115,7 @@
ActiveGestureLog.INSTANCE.addLog(
/* event= */ "startRecentsAnimation",
/* gestureEvent= */ START_RECENTS_ANIMATION);
+ mRecentsAnimationStartPending = true;
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -138,6 +145,7 @@
@Override
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
+ mRecentsAnimationStartPending = false;
if (mCallbacks == null) {
// It's possible for the recents animation to have finished and be cleaned up
// by the time we process the start callback, and in that case, just we can skip
@@ -178,11 +186,13 @@
@Override
public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
+ mRecentsAnimationStartPending = false;
cleanUpRecentsAnimation(newCallbacks);
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ mRecentsAnimationStartPending = false;
cleanUpRecentsAnimation(newCallbacks);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 55a7985..3735cbf 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -39,6 +39,7 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
+import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENTS_ANIMATION_START_PENDING;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
@@ -697,7 +698,7 @@
private void onInputEvent(InputEvent ev) {
if (!(ev instanceof MotionEvent)) {
ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
- .append("Cannot process input event, received unknown event ")
+ .append("Cannot process input event: received unknown event ")
.append(ev.toString()));
return;
}
@@ -710,22 +711,32 @@
if (!isUserUnlocked || (mDeviceState.isButtonNavMode()
&& !isTrackpadMotionEvent(event))) {
ActiveGestureLog.INSTANCE.addLog(new CompoundString("TIS.onInputEvent: ")
- .append("Cannot process input event, ")
+ .append("Cannot process input event: ")
.append(!isUserUnlocked
? "user is locked"
: "using 3-button nav and event is not a trackpad event"));
return;
}
- SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
-
final int action = event.getActionMasked();
// Note this will create a new consumer every mouse click, as after ACTION_UP from the click
// an ACTION_HOVER_ENTER will fire as well.
boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
&& isHoverActionWithoutConsumer(event);
+ if (mTaskAnimationManager.isRecentsAnimationStartPending()
+ && (action == ACTION_DOWN || isHoverActionWithoutConsumer)) {
+ ActiveGestureLog.INSTANCE.addLog(
+ new CompoundString("TIS.onInputEvent: ")
+ .append("Cannot process input event: a recents animation has been ")
+ .append("requested, but hasn't started."),
+ RECENTS_ANIMATION_START_PENDING);
+ return;
+ }
+
+ SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
+
CompoundString reasonString = action == ACTION_DOWN
- ? new CompoundString("onMotionEvent: ") : CompoundString.NO_OP;
+ ? new CompoundString("TIS.onMotionEvent: ") : CompoundString.NO_OP;
if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
mRotationTouchHelper.setOrientationTransformIfNeeded(event);
diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
index 4359294..e3772bd 100644
--- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
+++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java
@@ -39,7 +39,7 @@
SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT,
SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED,
FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, LAUNCHER_DESTROYED, RECENT_TASKS_MISSING,
- INVALID_VELOCITY_ON_SWIPE_UP,
+ INVALID_VELOCITY_ON_SWIPE_UP, RECENTS_ANIMATION_START_PENDING,
/**
* These GestureEvents are specifically associated to state flags that get set in
@@ -273,6 +273,14 @@
case START_RECENTS_ANIMATION:
lastStartRecentAnimationEventEntryTime = eventEntry.getTime();
break;
+ case RECENTS_ANIMATION_START_PENDING:
+ errorDetected |= printErrorIfTrue(
+ true,
+ prefix,
+ /* errorMessage= */ "new gesture attempted while a requested recents"
+ + " animation is still pending.",
+ writer);
+ break;
case EXPECTING_TASK_APPEARED:
case MOTION_DOWN:
case SET_END_TARGET:
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 065a9c5..0bb6b23 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -40,6 +40,7 @@
import android.view.RemoteAnimationTarget;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Utilities;
@@ -344,6 +345,14 @@
* Applies the target to the previously set parameters
*/
public void apply(TransformParams params) {
+ apply(params, null);
+ }
+
+ /**
+ * Applies the target to the previously set parameters, optionally with an overridden
+ * surface transaction
+ */
+ public void apply(TransformParams params, @Nullable SurfaceTransaction surfaceTransaction) {
if (mDp == null || mThumbnailPosition.isEmpty()) {
return;
}
@@ -404,7 +413,8 @@
mTempRectF.roundOut(mTmpCropRect);
params.setProgress(1f - fullScreenProgress);
- params.applySurfaceParams(params.createSurfaceParams(this));
+ params.applySurfaceParams(surfaceTransaction == null
+ ? params.createSurfaceParams(this) : surfaceTransaction);
if (!DEBUG) {
return;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 66d651e..d10541a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -542,6 +542,9 @@
private int mOverScrollShift = 0;
private long mScrollLastHapticTimestamp;
+ private int mKeyboardTaskFocusSnapAnimationDuration;
+ private int mKeyboardTaskFocusIndex = INVALID_PAGE;
+
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -1704,6 +1707,7 @@
// Removing views sets the currentPage to 0, so we save this and restore it after
// the new set of views are added
int previousCurrentPage = mCurrentPage;
+ int previousFocusedPage = indexOfChild(getFocusedChild());
removeAllViews();
// If we are entering Overview as a result of initiating a split from somewhere else
@@ -1833,6 +1837,8 @@
targetPage = indexOfChild(currentTaskView);
}
}
+ } else if (previousFocusedPage != INVALID_PAGE) {
+ targetPage = previousFocusedPage;
} else {
// Set the current page to the running task, but not if settling on new task.
if (hasAnyValidTaskIds(runningTaskId)) {
@@ -2914,7 +2920,7 @@
int focusedTaskShift = 0;
int focusedTaskWidthAndSpacing = 0;
int snappedTaskRowWidth = 0;
- int snappedPage = getNextPage();
+ int snappedPage = isKeyboardTaskFocusPending() ? mKeyboardTaskFocusIndex : getNextPage();
TaskView snappedTaskView = getTaskViewAt(snappedPage);
TaskView homeTaskView = getHomeTaskView();
TaskView nextFocusedTaskView = null;
@@ -5577,6 +5583,19 @@
}
/**
+ * Returns how many pixels the running task is offset on the currently laid out dominant axis
+ * specifically during a Keyboard task focus.
+ */
+ public int getScrollOffsetForKeyboardTaskFocus() {
+ if (!isKeyboardTaskFocusPending()) {
+ return getScrollOffset(getRunningTaskIndex());
+ }
+ return getPagedOrientationHandler().getPrimaryScroll(this)
+ - getScrollForPage(mKeyboardTaskFocusIndex)
+ + getScrollOffset(getRunningTaskIndex());
+ }
+
+ /**
* Sets whether or not we should clamp the scroll offset.
* This is used to avoid x-axis movement when swiping up transient taskbar.
* Should only be set at the beginning and end of the gesture, otherwise a jump may occur.
@@ -5609,14 +5628,14 @@
if (pageIndex == -1) {
return 0;
}
-
- int overScrollShift = getOverScrollShift();
- if (mAdjacentPageHorizontalOffset > 0) {
- // Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so
- // that the page can move freely given there's no visual indication why it shouldn't.
- overScrollShift = (int) Utilities.mapRange(mAdjacentPageHorizontalOffset,
- overScrollShift, getUndampedOverScrollShift());
- }
+ // Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so that
+ // the page can move freely given there's no visual indication why it shouldn't.
+ int overScrollShift = mAdjacentPageHorizontalOffset > 0
+ ? (int) Utilities.mapRange(
+ mAdjacentPageHorizontalOffset,
+ getOverScrollShift(),
+ getUndampedOverScrollShift())
+ : getOverScrollShift();
return getScrollForPage(pageIndex) - getPagedOrientationHandler().getPrimaryScroll(this)
+ overScrollShift + getOffsetFromScrollPosition(pageIndex);
}
@@ -6025,11 +6044,50 @@
dispatchScrollChanged();
}
+ /**
+ * Prepares this RecentsView to scroll properly for an upcoming child view focus request from
+ * keyboard quick switching
+ */
+ public void setKeyboardTaskFocusIndex(int taskIndex) {
+ mKeyboardTaskFocusIndex = taskIndex;
+ }
+
+ /** Returns whether this RecentsView will be scrolling to a child view for a focus request */
+ public boolean isKeyboardTaskFocusPending() {
+ return mKeyboardTaskFocusIndex != INVALID_PAGE;
+ }
+
+ private boolean isKeyboardTaskFocusPendingForChild(View child) {
+ return isKeyboardTaskFocusPending() && mKeyboardTaskFocusIndex == indexOfChild(child);
+ }
+
@Override
- protected boolean shouldHandleRequestChildFocus() {
- // If we are already scrolling to a task view, then the focus request has already been
- // handled
- return mScroller.isFinished();
+ protected int getSnapAnimationDuration() {
+ return isKeyboardTaskFocusPending()
+ ? mKeyboardTaskFocusSnapAnimationDuration : super.getSnapAnimationDuration();
+ }
+
+ @Override
+ protected void onVelocityValuesUpdated() {
+ super.onVelocityValuesUpdated();
+ mKeyboardTaskFocusSnapAnimationDuration =
+ getResources().getInteger(R.integer.config_keyboardTaskFocusSnapAnimationDuration);
+ }
+
+ @Override
+ protected boolean shouldHandleRequestChildFocus(View child) {
+ // If we are already scrolling to a task view and we aren't focusing to this child from
+ // keyboard quick switch, then the focus request has already been handled
+ return mScroller.isFinished() || isKeyboardTaskFocusPendingForChild(child);
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ if (isKeyboardTaskFocusPendingForChild(child)) {
+ updateGridProperties();
+ updateScrollSynchronously();
+ }
+ super.requestChildFocus(child, focused);
}
private void dispatchScrollChanged() {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
index 7191f70..36c591e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsKeyboardQuickSwitch.java
@@ -22,6 +22,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.tapl.KeyboardQuickSwitch;
+import com.android.launcher3.taskbar.KeyboardQuickSwitchController;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import org.junit.Assume;
@@ -49,7 +50,7 @@
DISMISS(0),
LAUNCH_LAST_APP(0),
LAUNCH_SELECTED_APP(1),
- LAUNCH_OVERVIEW(5);
+ LAUNCH_OVERVIEW(KeyboardQuickSwitchController.MAX_TASKS - 1);
private final int mNumAdditionalRunningTasks;
@@ -196,7 +197,9 @@
if (!testSurface.mInitialFocusAtZero) {
kqs.moveFocusBackward();
}
- kqs.launchFocusedOverviewTask();
+ kqs.launchFocusedOverviewTask()
+ // Check that the correct task was focused
+ .launchFocusedTaskByEnterKey(CALCULATOR_APP_PACKAGE);
break;
default:
throw new IllegalStateException("Cannot run test case: " + testCase);
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index e718d9c..bddfcfc 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -18,6 +18,9 @@
<!-- The duration of the PagedView page snap animation -->
<integer name="config_pageSnapAnimationDuration">550</integer>
+ <!-- The duration of the PagedView page snap animation -->
+ <integer name="config_keyboardTaskFocusSnapAnimationDuration">400</integer>
+
<!-- The duration of the Widget picker opening and closing animation -->
<integer name="config_bottomSheetOpenDuration">500</integer>
<integer name="config_bottomSheetCloseDuration">500</integer>
diff --git a/res/values/config.xml b/res/values/config.xml
index 29c4e66..1b74238 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -43,6 +43,9 @@
<!-- The duration of the PagedView page snap animation -->
<integer name="config_pageSnapAnimationDuration">750</integer>
+ <!-- The duration of the PagedView page snap animation -->
+ <integer name="config_keyboardTaskFocusSnapAnimationDuration">750</integer>
+
<!-- View tag key used to store SpringAnimation data. -->
<item type="id" name="spring_animation_tag" />
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 43dca5c..ca83245 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -637,6 +637,11 @@
mMinFlingVelocity = res.getDimensionPixelSize(R.dimen.min_fling_velocity);
mMinSnapVelocity = res.getDimensionPixelSize(R.dimen.min_page_snap_velocity);
mPageSnapAnimationDuration = res.getInteger(R.integer.config_pageSnapAnimationDuration);
+ onVelocityValuesUpdated();
+ }
+
+ protected void onVelocityValuesUpdated() {
+ // Overridden in RecentsView
}
@Override
@@ -1582,7 +1587,7 @@
@Override
public void requestChildFocus(View child, View focused) {
super.requestChildFocus(child, focused);
- if (!shouldHandleRequestChildFocus()) {
+ if (!shouldHandleRequestChildFocus(child)) {
return;
}
// In case the device is controlled by a controller, mCurrentPage isn't updated properly
@@ -1598,7 +1603,7 @@
}
}
- protected boolean shouldHandleRequestChildFocus() {
+ protected boolean shouldHandleRequestChildFocus(View child) {
return true;
}
@@ -1652,7 +1657,7 @@
}
protected void snapToDestination() {
- snapToPage(getDestinationPage(), mPageSnapAnimationDuration);
+ snapToPage(getDestinationPage(), getSnapAnimationDuration());
}
// We want the duration of the page snap animation to be influenced by the distance that
@@ -1676,7 +1681,7 @@
if (Math.abs(velocity) < mMinFlingVelocity) {
// If the velocity is low enough, then treat this more as an automatic page advance
// as opposed to an apparent physical response to flinging
- return snapToPage(whichPage, mPageSnapAnimationDuration);
+ return snapToPage(whichPage, getSnapAnimationDuration());
}
// Here we compute a "distance" that will be used in the computation of the overall
@@ -1698,12 +1703,16 @@
return snapToPage(whichPage, delta, duration);
}
+ protected int getSnapAnimationDuration() {
+ return mPageSnapAnimationDuration;
+ }
+
public boolean snapToPage(int whichPage) {
- return snapToPage(whichPage, mPageSnapAnimationDuration);
+ return snapToPage(whichPage, getSnapAnimationDuration());
}
public boolean snapToPageImmediately(int whichPage) {
- return snapToPage(whichPage, mPageSnapAnimationDuration, true);
+ return snapToPage(whichPage, getSnapAnimationDuration(), true);
}
public boolean snapToPage(int whichPage, int duration) {
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 930196d..ae2849e 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -1156,13 +1156,15 @@
applyAdapterSideAndBottomPaddings(grid);
+ MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
// Ignore left/right insets on tablet because we are already centered in-screen.
- if (grid.isPhone) {
- MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
+ if (grid.isTablet) {
+ mlp.leftMargin = mlp.rightMargin = 0;
+ } else {
mlp.leftMargin = insets.left;
mlp.rightMargin = insets.right;
- setLayoutParams(mlp);
}
+ setLayoutParams(mlp);
if (!grid.isVerticalBarLayout() || FeatureFlags.enableResponsiveWorkspace()) {
int topPadding = grid.allAppsPadding.top;
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 557ec48..6422943 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -109,7 +110,7 @@
itemInfo.bitmap = bitmapInfo;
itemInfo.contentDescription = context.getResources().getString(
com.android.launcher3.R.string.ps_add_button_content_description);
- itemInfo.runtimeStatusFlags |= FLAG_PRIVATE_SPACE_INSTALL_APP;
+ itemInfo.runtimeStatusFlags |= FLAG_PRIVATE_SPACE_INSTALL_APP | FLAG_NOT_PINNABLE;
BaseAllAppsAdapter.AdapterItem item = new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_ICON);
item.itemInfo = itemInfo;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 3ccde0a..b6e5977 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -16,6 +16,8 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
+
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -29,8 +31,10 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.Flags;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.ActivityContext;
@@ -221,6 +225,12 @@
}
}
+ protected boolean isItemPinnable() {
+ return !Flags.privateSpaceRestrictItemDrag()
+ || !(mDragObject.dragInfo instanceof ItemInfoWithIcon itemInfoWithIcon)
+ || (itemInfoWithIcon.runtimeStatusFlags & FLAG_NOT_PINNABLE) == 0;
+ }
+
public Optional<InstanceId> getLogInstanceId() {
return Optional.ofNullable(mDragObject)
.map(dragObject -> dragObject.logInstanceId);
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index da6f446..f3708a2 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -149,9 +149,10 @@
handleMoveEvent(mLastTouch.x, mLastTouch.y);
- if (!mActivity.isTouchInProgress() && options.simulatedDndStartPoint == null) {
+ if (!isItemPinnable()
+ || (!mActivity.isTouchInProgress() && options.simulatedDndStartPoint == null)) {
// If it is an internal drag and the touch is already complete, cancel immediately
- MAIN_EXECUTOR.submit(this::cancelDrag);
+ MAIN_EXECUTOR.post(this::cancelDrag);
}
return dragView;
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index c9c5fd3..1c9db17 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -44,6 +45,7 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -53,6 +55,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -204,17 +207,21 @@
.collect(Collectors.toList());
container = (PopupContainerWithArrow) launcher.getLayoutInflater().inflate(
R.layout.popup_container, launcher.getDragLayer(), false);
- container.configureForLauncher(launcher);
+ container.configureForLauncher(launcher, item);
container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
container.requestFocus();
return container;
}
- private void configureForLauncher(Launcher launcher) {
+ private void configureForLauncher(Launcher launcher, ItemInfo itemInfo) {
addOnAttachStateChangeListener(new LauncherPopupLiveUpdateHandler(
launcher, (PopupContainerWithArrow<Launcher>) this));
- mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
+ if (!Flags.privateSpaceRestrictItemDrag()
+ || !(itemInfo instanceof ItemInfoWithIcon itemInfoWithIcon)
+ || (itemInfoWithIcon.runtimeStatusFlags & FLAG_NOT_PINNABLE) == 0) {
+ mPopupItemDragHandler = new LauncherPopupItemDragHandler(launcher, this);
+ }
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(launcher);
launcher.getDragController().addDragListener(this);
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
index 8d1d96b..79b25a4 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragController.java
@@ -16,6 +16,8 @@
package com.android.launcher3.secondarydisplay;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -129,6 +131,10 @@
dragView.show(mLastTouch.x, mLastTouch.y);
mDistanceSinceScroll = 0;
+ if (!isItemPinnable()) {
+ MAIN_EXECUTOR.post(this:: cancelDrag);
+ }
+
if (!mIsInPreDrag) {
callOnDragStart();
} else if (mOptions.preDragCondition != null) {
diff --git a/src/com/android/launcher3/widget/picker/OWNERS b/src/com/android/launcher3/widget/picker/OWNERS
new file mode 100644
index 0000000..6aabbfa
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/OWNERS
@@ -0,0 +1,16 @@
+set noparent
+
+# Bug component: 1481801
+
+# People who can approve changes for submission
+#
+
+# Widget Picker OWNERS
+zakcohen@google.com
+shamalip@google.com
+wvk@google.com
+
+# Launcher OWNERS
+captaincole@google.com
+sunnygoyal@google.com
+
diff --git a/tests/multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 3a54915..8eebdb2 100644
--- a/tests/multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/multivalentTests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -44,7 +44,9 @@
@Override
public void evaluate() throws Throwable {
try {
+ // we expect to begin unlocked...
AbstractLauncherUiTest.verifyKeyguardInvisible();
+
mTest.mDevice.pressHome();
mTest.waitForLauncherCondition("Launcher activity wasn't created",
launcher -> launcher != null);
@@ -67,6 +69,9 @@
}
});
mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0);
+
+ // and end unlocked...
+ AbstractLauncherUiTest.verifyKeyguardInvisible();
}
}
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 1bc489c..d337b91 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -19,9 +19,11 @@
import static android.view.KeyEvent.KEYCODE_ESCAPE;
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
+import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
import android.graphics.Rect;
+import android.view.KeyEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -47,6 +49,10 @@
"Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
+ private static final Pattern EVENT_ENTER_DOWN = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ENTER");
+ private static final Pattern EVENT_ENTER_UP = Pattern.compile(
+ "Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ENTER");
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
@@ -414,6 +420,31 @@
}
}
+ /**
+ * Presses the enter key to launch the focused task
+ * <p>
+ * If no task is focused, this will fail.
+ */
+ public LaunchedAppState launchFocusedTaskByEnterKey(@NonNull String expectedPackageName) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ENTER_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ENTER_UP);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
+
+ mLauncher.executeAndWaitForLauncherStop(
+ () -> mLauncher.assertTrue(
+ "Failed to press enter",
+ mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_ENTER)),
+ "pressing enter");
+ mLauncher.assertAppLaunched(expectedPackageName);
+
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "pressed enter")) {
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
private void verifyActionsViewVisibility() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to assert overview actions view visibility")) {
diff --git a/tests/src/com/android/launcher3/widget/picker/OWNERS b/tests/src/com/android/launcher3/widget/picker/OWNERS
new file mode 100644
index 0000000..775b0c7
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/picker/OWNERS
@@ -0,0 +1,18 @@
+set noparent
+
+# Bug component: 1481801
+# People who can approve changes for submission
+#
+
+# Widget Picker OWNERS
+zakcohen@google.com
+shamalip@google.com
+wvk@google.com
+
+# For Tests
+vadimt@google.com
+
+# Launcher OWNERS
+captaincole@google.com
+sunnygoyal@google.com
+