diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 2ccec44fe2..a83f51ade9 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -175,6 +175,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener public static final int RECENTS_LAUNCH_DURATION = 336; private static final int LAUNCHER_RESUME_START_DELAY = 100; private static final int CLOSING_TRANSITION_DURATION_MS = 250; + public static final int SPLIT_LAUNCH_DURATION = 370; public static final int CONTENT_ALPHA_DURATION = 217; protected static final int CONTENT_SCALE_DURATION = 350; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 7a510ac95f..a2e4c225d9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -166,7 +166,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ new StashedHandleViewController(this, stashedHandleView), new TaskbarStashController(this), new TaskbarEduController(this), - new TaskbarAutohideSuspendController(this)); + new TaskbarAutohideSuspendController(this), + new TaskbarPopupController()); } public void init(TaskbarSharedState sharedState) { @@ -255,6 +256,11 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ return false; } + @Override + public View.OnClickListener getItemOnClickListener() { + return this::onTaskbarIconClicked; + } + /** * Change from hotseat/predicted hotseat to taskbar container. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java index 08a79c064d..56730dbd70 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java @@ -38,6 +38,7 @@ public class TaskbarControllers { public final TaskbarStashController taskbarStashController; public final TaskbarEduController taskbarEduController; public final TaskbarAutohideSuspendController taskbarAutohideSuspendController; + public final TaskbarPopupController taskbarPopupController; /** Do not store this controller, as it may change at runtime. */ @NonNull public TaskbarUIController uiController = TaskbarUIController.DEFAULT; @@ -55,7 +56,8 @@ public class TaskbarControllers { StashedHandleViewController stashedHandleViewController, TaskbarStashController taskbarStashController, TaskbarEduController taskbarEduController, - TaskbarAutohideSuspendController taskbarAutoHideSuspendController) { + TaskbarAutohideSuspendController taskbarAutoHideSuspendController, + TaskbarPopupController taskbarPopupController) { this.taskbarActivityContext = taskbarActivityContext; this.taskbarDragController = taskbarDragController; this.navButtonController = navButtonController; @@ -70,6 +72,7 @@ public class TaskbarControllers { this.taskbarStashController = taskbarStashController; this.taskbarEduController = taskbarEduController; this.taskbarAutohideSuspendController = taskbarAutoHideSuspendController; + this.taskbarPopupController = taskbarPopupController; } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index 3bcacb6c8a..b3a9f8db7c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -40,6 +40,7 @@ import com.android.launcher3.DropTarget; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.accessibility.DragViewStateAnnouncer; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragDriver; import com.android.launcher3.dragndrop.DragOptions; @@ -49,6 +50,7 @@ import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.popup.PopupContainerWithArrow; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ClipDescriptionCompat; import com.android.systemui.shared.system.LauncherAppsCompat; @@ -139,11 +141,13 @@ public class TaskbarDragController extends DragController popupContainer = + mControllers.taskbarPopupController.showForIcon(btv); + if (popupContainer != null) { + dragOptions.preDragCondition = popupContainer.createPreDragCondition(); + } + } startDrag( drawable, diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java index 5e76b960fd..6dcfe564a6 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java @@ -23,6 +23,7 @@ import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; @@ -30,6 +31,7 @@ import com.android.launcher3.util.LauncherBindableItemsContainer; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -186,4 +188,9 @@ public class TaskbarModelCallbacks implements TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, isHotseatEmpty); mControllers.taskbarStashController.applyState(); } + + @Override + public void bindDeepShortcutMap(HashMap deepShortcutMapCopy) { + mControllers.taskbarPopupController.setDeepShortcutMap(deepShortcutMapCopy); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java new file mode 100644 index 0000000000..952f597f72 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java @@ -0,0 +1,90 @@ +/* + * 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.launcher3.taskbar; + +import androidx.annotation.NonNull; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.popup.PopupContainerWithArrow; +import com.android.launcher3.popup.PopupDataProvider; +import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.views.ActivityContext; + +import java.util.HashMap; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Implements interfaces required to show and allow interacting with a PopupContainerWithArrow. + */ +public class TaskbarPopupController { + + private static final SystemShortcut.Factory + APP_INFO = SystemShortcut.AppInfo::new; + + private final PopupDataProvider mPopupDataProvider; + + public TaskbarPopupController() { + // TODO (b/198438631): add notifications dots change listener + mPopupDataProvider = new PopupDataProvider(packageUserKey -> {}); + } + + @NonNull + public PopupDataProvider getPopupDataProvider() { + return mPopupDataProvider; + } + + public void setDeepShortcutMap(HashMap deepShortcutMapCopy) { + mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy); + } + + /** + * Shows the notifications and deep shortcuts associated with a Taskbar {@param icon}. + * @return the container if shown or null. + */ + public PopupContainerWithArrow showForIcon(BubbleTextView icon) { + TaskbarActivityContext context = ActivityContext.lookupContext(icon.getContext()); + if (PopupContainerWithArrow.getOpen(context) != null) { + // There is already an items container open, so don't open this one. + icon.clearFocus(); + return null; + } + ItemInfo item = (ItemInfo) icon.getTag(); + if (!PopupContainerWithArrow.canShow(icon, item)) { + return null; + } + + final PopupContainerWithArrow container = + (PopupContainerWithArrow) context.getLayoutInflater().inflate( + R.layout.popup_container, context.getDragLayer(), false); + // TODO (b/198438631): configure for taskbar/context + + container.populateAndShow(icon, + mPopupDataProvider.getShortcutCountForItem(item), + mPopupDataProvider.getNotificationKeysForItem(item), + // TODO (b/198438631): add support for INSTALL shortcut factory + Stream.of(APP_INFO) + .map(s -> s.getShortcut(context, item)) + .filter(Objects::nonNull) + .collect(Collectors.toList())); + container.requestFocus(); + return container; + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 09197c3240..c47bde9dcd 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -261,7 +261,7 @@ public class TaskbarViewController { private boolean mCanceledStashHint; public View.OnClickListener getIconOnClickListener() { - return mActivity::onTaskbarIconClicked; + return mActivity.getItemOnClickListener(); } public View.OnLongClickListener getIconOnLongClickListener() { diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 12b071d77c..15729e11fb 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -28,6 +28,7 @@ import static com.android.launcher3.QuickstepTransitionManager.ANIMATION_NAV_FAD import static com.android.launcher3.QuickstepTransitionManager.NAV_FADE_IN_INTERPOLATOR; import static com.android.launcher3.QuickstepTransitionManager.NAV_FADE_OUT_INTERPOLATOR; import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION; +import static com.android.launcher3.QuickstepTransitionManager.SPLIT_LAUNCH_DURATION; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; @@ -459,6 +460,7 @@ public final class TaskViewUtils { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.setDuration(SPLIT_LAUNCH_DURATION); animator.addUpdateListener(valueAnimator -> { float progress = valueAnimator.getAnimatedFraction(); for (SurfaceControl leash: openingTargets) { @@ -559,7 +561,7 @@ public final class TaskViewUtils { anim.addListener(windowAnimEndListener); } - static void setSplitAuxiliarySurfacesShown(RemoteAnimationTargetCompat[] nonApps, + public static void setSplitAuxiliarySurfacesShown(RemoteAnimationTargetCompat[] nonApps, boolean shown) { // TODO(b/182592057): make this part of the animations instead. if (nonApps != null && nonApps.length > 0) { diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index b5238c64b7..33ba7dd9e7 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4241,6 +4241,10 @@ public abstract class RecentsView { if (isSuccess) { + if (tv.getTaskIds()[1] != -1) { + TaskViewUtils.setSplitAuxiliarySurfacesShown(mRemoteTargetHandles[0] + .getTransformParams().getTargetSet().nonApps, true); + } if (ENABLE_QUICKSTEP_LIVE_TILE.get() && tv.isRunningTask()) { finishRecentsAnimation(false /* toRecents */, null); onTaskLaunchAnimationEnd(true /* success */); diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 13583874b9..d3629947c8 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -204,6 +204,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_TASKBAR_EDU = getDebugFlag("ENABLE_TASKBAR_EDU", true, "Enables showing taskbar education the first time an app is opened."); + public static final BooleanFlag ENABLE_TASKBAR_POPUP_MENU = getDebugFlag( + "ENABLE_TASKBAR_POPUP_MENU", false, "Enables long pressing taskbar icons to show the" + + " popup menu."); + public static final BooleanFlag ENABLE_OVERVIEW_GRID = getDebugFlag( "ENABLE_OVERVIEW_GRID", true, "Uses grid overview layout. " + "Only applicable on large screen devices."); diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java index b34af975bd..1767939114 100644 --- a/src/com/android/launcher3/statemanager/StateManager.java +++ b/src/com/android/launcher3/statemanager/StateManager.java @@ -84,7 +84,7 @@ public class StateManager> { + ", mCurrentStableState:" + mCurrentStableState + ", mState:" + mState + ", mRestState:" + mRestState - + ", isInTransition:" + (mConfig.currentAnimation != null) + ")"; + + ", isInTransition:" + isInTransition() + ")"; } public void dump(String prefix, PrintWriter writer) { @@ -93,7 +93,7 @@ public class StateManager> { writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState); writer.println(prefix + "\tmState:" + mState); writer.println(prefix + "\tmRestState:" + mRestState); - writer.println(prefix + "\tisInTransition:" + (mConfig.currentAnimation != null)); + writer.println(prefix + "\tisInTransition:" + isInTransition()); } public StateHandler[] getStateHandlers() { @@ -129,6 +129,13 @@ public class StateManager> { && (mConfig.targetState == null || mConfig.targetState == state); } + /** + * @return {@code true} If there is an active transition. + */ + public boolean isInTransition() { + return mConfig.currentAnimation != null; + } + /** * @see #goToState(STATE_TYPE, boolean, AnimatorListener) */ diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 17d925c1f9..8ebfd62b69 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -157,6 +157,12 @@ public class TestInformationHandler implements ResourceBasedOverride { return response; } + case TestProtocol.REQUEST_ENABLE_ROTATION: + MAIN_EXECUTOR.submit(() -> + Launcher.ACTIVITY_TRACKER.getCreatedActivity().getRotationHelper() + .forceAllowRotationForTesting(Boolean.parseBoolean(arg))); + return null; + default: return null; } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 91c7221313..9a74fb1577 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -108,6 +108,7 @@ public final class TestProtocol { "get-focused-task-height-for-tablet"; public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET = "get-grid-task-size-rect-for-tablet"; + public static final String REQUEST_ENABLE_ROTATION = "enable_rotation"; public static Long sForcePauseTimeout; public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout"; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index b3457cda75..44f2719a02 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -354,9 +354,10 @@ public abstract class AbstractLauncherUiTest { // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call // expecting the results of that gesture because the wait can hide flakeness. - protected void waitForStableState(String message, Supplier state) { + protected void waitForStateTransitionToEnd(String message, Supplier state) { waitForLauncherCondition(message, - launcher -> launcher.getStateManager().isInStableState(state.get())); + launcher -> launcher.getStateManager().isInStableState(state.get()) + && !launcher.getStateManager().isInTransition()); } protected void waitForResumed(String message) { diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java index 2087bfe8c6..aca5951e64 100644 --- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java +++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java @@ -91,9 +91,11 @@ public class WorkProfileTest extends AbstractLauncherUiTest { public void workTabExists() { mDevice.pressHome(); waitForLauncherCondition("Launcher didn't start", Objects::nonNull); - waitForStableState("Launcher internal state didn't switch to Normal", () -> NORMAL); + waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal", + () -> NORMAL); executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS)); - waitForStableState("Launcher internal state didn't switch to All Apps", () -> 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); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index e2d023825c..5a807caa13 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -350,6 +350,10 @@ public final class LauncherInstrumentation { getTestInfo(TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT, Long.toString(timeout)); } + public void setEnableRotation(boolean on) { + getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on)); + } + public boolean hadNontestEvents() { return getTestInfo(TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS) .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);