From 6a4e491da121b560fca96686b16f3278fb48981a Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Wed, 19 Jul 2023 12:26:35 +0100 Subject: [PATCH 01/14] Prevent showing action button edu tooltip in grid only overview. Fix: 291232225 Test: OverviewActionsControllerTest. Flag: ENABLE_GRID_ONLY_OVERVIEW Change-Id: I3d2881fea26ddfdda8540e3609733f6261e40458 --- .../com/android/quickstep/views/OverviewActionsView.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index e47c0893e5..b31791a9a0 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -257,6 +257,13 @@ public class OverviewActionsView extends FrameLayo return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA); } + /** + * Returns the visibility of the overview actions buttons. + */ + public @Visibility int getActionsButtonVisibility() { + return findViewById(R.id.action_buttons).getVisibility(); + } + /** * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar. */ From 86e18b59a9a7f4b5eea4328d0fe9cc71fa31dc08 Mon Sep 17 00:00:00 2001 From: Luca Zuccarini Date: Fri, 21 Jul 2023 13:14:24 +0000 Subject: [PATCH 02/14] Update default value for container launch animations to TEAMFOOD. Also move flag checking outside ItemInfo. Bug: 262778448 Flag: ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES Flag: ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION Test: manual Change-Id: I49de8a93539829e41c1de1fb23630c1b2ca8b646 --- src/com/android/launcher3/config/FeatureFlags.java | 4 ++-- src/com/android/launcher3/model/data/ItemInfo.java | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index e3e14009ee..542e26d097 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -83,11 +83,11 @@ public final class FeatureFlags { */ // TODO(Block 1): Clean up flags public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = getReleaseFlag( - 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", DISABLED, + 270394041, "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", TEAMFOOD, "Enable option to replace decorator-based search result backgrounds with drawables"); public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = getReleaseFlag( - 270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", DISABLED, + 270394392, "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", TEAMFOOD, "Enable option to launch search results using the new view container transitions"); // TODO(Block 2): Clean up flags diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index ba1547f4be..9afa4593a9 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -49,7 +49,6 @@ import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Animation; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Workspace; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logger.LauncherAtom.AllAppsContainer; import com.android.launcher3.logger.LauncherAtom.ContainerInfo; @@ -323,9 +322,7 @@ public class ItemInfo { * Returns whether this item should use the background animation. */ public boolean shouldUseBackgroundAnimation() { - return animationType == LauncherSettings.Animation.VIEW_BACKGROUND - && FeatureFlags.ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES.get() - && FeatureFlags.ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION.get(); + return animationType == LauncherSettings.Animation.VIEW_BACKGROUND; } /** From cf25610a288e0a333a36a77d5c52aec4883df989 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Tue, 25 Jul 2023 14:20:00 -0700 Subject: [PATCH 03/14] Flag stashing desktop when going home Put logic to stash desktop apps behind a flag. Turn it off by default. Bug: 292109910 Flag: persist.wm.debug.desktop_stashing Test: turn off the flag, open an app on the desktop and go home observe that user is taken to home screen and subsequent launches are in full screen turn on the flag, open app on the desktop and go home observe that there is a toast shown for desktop and next app is launched to desktop Change-Id: Ic6f9a3644bb09d4fb877f4694843a7c20969f925 --- .../statehandlers/DesktopVisibilityController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java index 7283a184d7..dc4441d6c4 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -39,7 +39,8 @@ public class DesktopVisibilityController { private static final String TAG = "DesktopVisController"; private static final boolean DEBUG = false; - + private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean( + "persist.wm.debug.desktop_stashing", false); private final Launcher mLauncher; private boolean mFreeformTasksVisible; @@ -73,6 +74,9 @@ public class DesktopVisibilityController { @Override public void onStashedChanged(int displayId, boolean stashed) { + if (!IS_STASHING_ENABLED) { + return; + } MAIN_EXECUTOR.execute(() -> { if (displayId == mLauncher.getDisplayId()) { if (DEBUG) { @@ -189,7 +193,7 @@ public class DesktopVisibilityController { * Handle launcher moving to home due to home gesture or home button press. */ public void onHomeActionTriggered() { - if (areFreeformTasksVisible()) { + if (IS_STASHING_ENABLED && areFreeformTasksVisible()) { SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId()); } } From a1d0d8269ccc35b56dc4ca3d6922070fc2257ed5 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 27 Jul 2023 14:11:27 -0700 Subject: [PATCH 04/14] Remove split instructions view on split launch success * We weren't removing the instance of the view that is created when split initiated from workspace * Fewer bugs like this once RecentsView + workspace instructions view are consolidated Bug: 292590703 Flag: ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE Test: Swiping to home after split apps launch hides the view Change-Id: I6b5037c024e6e0c456b687bc802076f691a955b2 --- .../com/android/launcher3/uioverrides/QuickstepLauncher.java | 2 ++ .../com/android/quickstep/util/SplitAnimationController.kt | 5 +++++ .../android/quickstep/util/SplitToWorkspaceController.java | 1 + quickstep/src/com/android/quickstep/views/RecentsView.java | 2 ++ 4 files changed, 10 insertions(+) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index ffd22b89e2..1e1bff3340 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -664,6 +664,8 @@ public class QuickstepLauncher extends Launcher { @Override public void onAnimationCancel(Animator animation) { getDragLayer().removeView(floatingTaskView); + mSplitSelectStateController.getSplitAnimationController() + .removeSplitInstructionsView(QuickstepLauncher.this); mSplitSelectStateController.resetState(); } }); diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt index 56d68570ac..bcb9cecb1d 100644 --- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt +++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt @@ -265,6 +265,11 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC return anim } + /** Removes the split instructions view from [launcher] drag layer. */ + fun removeSplitInstructionsView(launcher: StatefulActivity<*>) { + safeRemoveViewFromDragLayer(launcher, splitInstructionsView) + } + private fun safeRemoveViewFromDragLayer(launcher: StatefulActivity<*>, view: View?) { if (view != null) { launcher.dragLayer.removeView(view) diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java index 148a45a386..b36cf5f5b4 100644 --- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java +++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java @@ -169,6 +169,7 @@ public class SplitToWorkspaceController { private void cleanUp() { mLauncher.getDragLayer().removeView(firstFloatingTaskView); mLauncher.getDragLayer().removeView(secondFloatingTaskView); + mController.getSplitAnimationController().removeSplitInstructionsView(mLauncher); mController.resetState(); } }); diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index a21bbe10f2..4da979e673 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4823,6 +4823,8 @@ public abstract class RecentsView Date: Tue, 11 Jul 2023 17:10:34 -0700 Subject: [PATCH 05/14] Make some things public for reuse. Test: With sister change. Flag: N/A Bug: 292000892 Change-Id: I8a20b9d4d5df748f90d907a10fe99d066d700170 --- .../src/com/android/launcher3/QuickstepTransitionManager.java | 2 +- quickstep/src/com/android/quickstep/TaskOverlayFactory.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 21f4e9b8ce..5eb06d8b49 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -183,7 +183,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; - private static final long APP_LAUNCH_DURATION = 500; + public static final long APP_LAUNCH_DURATION = 500; private static final long APP_LAUNCH_ALPHA_DURATION = 50; private static final long APP_LAUNCH_ALPHA_START_DELAY = 25; diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java index 1744b08cf5..06f1f9a6f4 100644 --- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java +++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java @@ -159,6 +159,10 @@ public class TaskOverlayFactory implements ResourceBasedOverride { return mActionsView; } + public TaskThumbnailView getThumbnailView() { + return mThumbnailView; + } + /** * Called when the current task is interactive for the user */ From 7d72bcd46e36558bf838a815bf0eac7b45161fdd Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Fri, 28 Jul 2023 17:33:00 -0700 Subject: [PATCH 06/14] Fix tap on navbar handle when on desktop Tapping on navbar handle was hiding the stashed taskbar handle. When tappin on navbar handle, it starts the recents animation. Which gets cancelled. Normally, the fullscreen app being visible, it means launcher activity will be paused after. But when on desktop, launcher is visible in the background and we need to manually set it to paused state. When recents gesture is cancelled after navbar handle tap, the gesture end state is null. Detect this in DesktopVisibiltyController and mark launcher as paused in this case. Bug: 286140120 Flag: persist.wm.debug.desktop_mode_2 Test: open an app on desktop, tap on navbar, observe that user remains on desktop Change-Id: Iee915026265721d42a0b722d6b1595521f20a59a --- .../DesktopVisibilityController.java | 33 +++++++++++++++---- .../uioverrides/QuickstepLauncher.java | 2 +- .../quickstep/views/LauncherRecentsView.java | 8 +++-- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java index 7283a184d7..719fdc9755 100644 --- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java +++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java @@ -27,6 +27,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.uioverrides.QuickstepLauncher; +import com.android.quickstep.GestureState; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.views.DesktopAppSelectView; import com.android.wm.shell.desktopmode.IDesktopTaskListener; @@ -166,20 +167,40 @@ public class DesktopVisibilityController { /** * Whether recents gesture is currently in progress. */ - public boolean isGestureInProgress() { + public boolean isRecentsGestureInProgress() { return mGestureInProgress; } /** - * Sets whether recents gesture is in progress. + * Notify controller that recents gesture has started. */ - public void setGestureInProgress(boolean gestureInProgress) { - if (DEBUG) { - Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress); - } + public void setRecentsGestureStart() { if (!isDesktopModeSupported()) { return; } + setRecentsGestureInProgress(true); + } + + /** + * Notify controller that recents gesture finished with the given + * {@link com.android.quickstep.GestureState.GestureEndTarget} + */ + public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) { + if (!isDesktopModeSupported()) { + return; + } + setRecentsGestureInProgress(false); + + if (endTarget == null) { + // Gesture did not result in a new end target. Ensure launchers gets paused again. + markLauncherPaused(); + } + } + + private void setRecentsGestureInProgress(boolean gestureInProgress) { + if (DEBUG) { + Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress); + } if (gestureInProgress != mGestureInProgress) { mGestureInProgress = gestureInProgress; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index ffd22b89e2..c41dee5fff 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -865,7 +865,7 @@ public class QuickstepLauncher extends Launcher { if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) { DesktopVisibilityController controller = mDesktopVisibilityController; if (controller != null && controller.areFreeformTasksVisible() - && !controller.isGestureInProgress()) { + && !controller.isRecentsGestureInProgress()) { // Return early to skip setting activity to appear as resumed // TODO(b/255649902): shouldn't be needed when we have a separate launcher state // for desktop that we can use to control other parts of launcher diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 80e5a54b8d..c4407182ba 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -242,7 +242,7 @@ public class LauncherRecentsView extends RecentsView Date: Fri, 28 Jul 2023 18:08:38 -0700 Subject: [PATCH 07/14] Ignoring alpha jumps in ArrowTipView and its children. I've seen an alpha jump in ArrowTipView|TextView:id/text, so disabling the whole view sub-tree. Bug: 292561338 Test: presubmit Flag: N/A Change-Id: I49e5d16d7c6e772bb848061087c56221b113d579 --- .../launcher3/util/viewcapture_analysis/AlphaJumpDetector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java index cb404b966c..75e25be97f 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java @@ -113,7 +113,7 @@ final class AlphaJumpDetector extends AnomalyDetector { DRAG_LAYER + "WidgetsFullSheet|SpringRelativeLayout:id/container", DRAG_LAYER + "WidgetsTwoPaneSheet|SpringRelativeLayout:id/container", CONTENT + "LauncherRootView:id/launcher|FloatingIconView", - RECENTS_DRAG_LAYER + "ArrowTipView|View:id/arrow", + RECENTS_DRAG_LAYER + "ArrowTipView", DRAG_LAYER + "FallbackRecentsView:id/overview_panel", RECENTS_DRAG_LAYER + "FallbackRecentsView:id/overview_panel", DRAG_LAYER From e0a3da08ee3fed5de422ebc0c85895a923b53d1d Mon Sep 17 00:00:00 2001 From: fbaron Date: Thu, 27 Jul 2023 11:00:25 -0700 Subject: [PATCH 08/14] Update the launcher test app widgets to include a custom icon instead of the default icon Bug: 292287927 Test: no test Flag: no flag Change-Id: I98b87029a7afdcaf85e40accda03a145b213136c --- tests/AndroidManifest-common.xml | 4 +++ .../test_widget_dynamic_colors_icon.xml | 29 +++++++++++++++++++ .../drawable/test_widget_no_config_icon.xml | 29 +++++++++++++++++++ .../drawable/test_widget_with_config_icon.xml | 29 +++++++++++++++++++ .../drawable/test_widget_with_dialog_icon.xml | 29 +++++++++++++++++++ 5 files changed, 120 insertions(+) create mode 100644 tests/res/drawable/test_widget_dynamic_colors_icon.xml create mode 100644 tests/res/drawable/test_widget_no_config_icon.xml create mode 100644 tests/res/drawable/test_widget_with_config_icon.xml create mode 100644 tests/res/drawable/test_widget_with_dialog_icon.xml diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 28688fd6fc..bb61fbe440 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -32,6 +32,7 @@ @@ -65,6 +66,7 @@ @@ -76,6 +78,7 @@ @@ -87,6 +90,7 @@ diff --git a/tests/res/drawable/test_widget_dynamic_colors_icon.xml b/tests/res/drawable/test_widget_dynamic_colors_icon.xml new file mode 100644 index 0000000000..69f66757f6 --- /dev/null +++ b/tests/res/drawable/test_widget_dynamic_colors_icon.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/tests/res/drawable/test_widget_no_config_icon.xml b/tests/res/drawable/test_widget_no_config_icon.xml new file mode 100644 index 0000000000..e3d012529d --- /dev/null +++ b/tests/res/drawable/test_widget_no_config_icon.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/tests/res/drawable/test_widget_with_config_icon.xml b/tests/res/drawable/test_widget_with_config_icon.xml new file mode 100644 index 0000000000..98b797b870 --- /dev/null +++ b/tests/res/drawable/test_widget_with_config_icon.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/tests/res/drawable/test_widget_with_dialog_icon.xml b/tests/res/drawable/test_widget_with_dialog_icon.xml new file mode 100644 index 0000000000..d2879d281b --- /dev/null +++ b/tests/res/drawable/test_widget_with_dialog_icon.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + From df59c2e5352f9dd8c7cbfba480a3d886639b05a4 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Mon, 17 Jul 2023 14:59:57 -0700 Subject: [PATCH 09/14] Make AllAppsRecyclerViewContainer GONE when hidden Test: N/A Flags: ALL_APPS_GONE_VISIBILITY Bug: 264341825 Change-Id: I28933c08e80b95bcd5031da9869b37f34f6408b2 --- .../allapps/ActivityAllAppsContainerView.java | 54 +++++++++++++------ .../allapps/AllAppsRecyclerView.java | 15 +++++- .../allapps/AllAppsTransitionController.java | 3 +- .../launcher3/anim/AlphaUpdateListener.java | 14 ++++- .../launcher3/config/FeatureFlags.java | 9 +++- .../recyclerview/AllAppsRecyclerViewPool.kt | 17 ++++-- .../launcher3/util/MultiValueAlpha.java | 9 +++- 7 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java index 4c86becfed..259c5cc553 100644 --- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java @@ -18,6 +18,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH; import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD; import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD; +import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY; import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_RV_PREINFLATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_COUNT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB; @@ -551,17 +552,14 @@ public class ActivityAllAppsContainerView mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.WORK).mRecyclerView); mAllAppsStore.unregisterIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView); + final AllAppsRecyclerView mainRecyclerView; + final AllAppsRecyclerView workRecyclerView; if (mUsingTabs) { - mAH.get(AdapterHolder.MAIN).setup(mViewPager.getChildAt(0), mPersonalMatcher); - mAH.get(AdapterHolder.WORK).setup(mViewPager.getChildAt(1), mWorkManager.getMatcher()); - mAH.get(AdapterHolder.WORK).mRecyclerView.setId(R.id.apps_list_view_work); - if (ENABLE_ALL_APPS_RV_PREINFLATION.get()) { - // Let main and work rv share same view pool. - ((RecyclerView) mViewPager.getChildAt(0)) - .setRecycledViewPool(mAllAppsStore.getRecyclerViewPool()); - ((RecyclerView) mViewPager.getChildAt(1)) - .setRecycledViewPool(mAllAppsStore.getRecyclerViewPool()); - } + mainRecyclerView = (AllAppsRecyclerView) mViewPager.getChildAt(0); + workRecyclerView = (AllAppsRecyclerView) mViewPager.getChildAt(1); + mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, mPersonalMatcher); + mAH.get(AdapterHolder.WORK).setup(workRecyclerView, mWorkManager.getMatcher()); + workRecyclerView.setId(R.id.apps_list_view_work); if (FeatureFlags.ENABLE_EXPANDING_PAUSE_WORK_BUTTON.get()) { mAH.get(AdapterHolder.WORK).mRecyclerView.addOnScrollListener( mWorkManager.newScrollListener()); @@ -586,13 +584,15 @@ public class ActivityAllAppsContainerView onActivePageChanged(mViewPager.getNextPage()); } } else { - mAH.get(AdapterHolder.MAIN).setup(findViewById(R.id.apps_list_view), null); + mainRecyclerView = findViewById(R.id.apps_list_view); + workRecyclerView = null; + mAH.get(AdapterHolder.MAIN).setup(mainRecyclerView, null); mAH.get(AdapterHolder.WORK).mRecyclerView = null; - if (ENABLE_ALL_APPS_RV_PREINFLATION.get()) { - mAH.get(AdapterHolder.MAIN).mRecyclerView - .setRecycledViewPool(mAllAppsStore.getRecyclerViewPool()); - } } + setUpCustomRecyclerViewPool( + mainRecyclerView, + workRecyclerView, + mAllAppsStore.getRecyclerViewPool()); setupHeader(); if (isSearchBarFloating()) { @@ -609,6 +609,30 @@ public class ActivityAllAppsContainerView mAllAppsStore.registerIconContainer(mAH.get(AdapterHolder.SEARCH).mRecyclerView); } + /** + * If {@link ENABLE_ALL_APPS_RV_PREINFLATION} is enabled, wire custom + * {@link RecyclerView.RecycledViewPool} to main and work {@link AllAppsRecyclerView}. + * + * Then if {@link ALL_APPS_GONE_VISIBILITY} is enabled, update max pool size. This is because + * all apps rv's hidden visibility is changed to {@link View#GONE} from {@link View#INVISIBLE), + * thus we cannot rely on layout pass to update pool size. + */ + private static void setUpCustomRecyclerViewPool( + @NonNull AllAppsRecyclerView mainRecyclerView, + @Nullable AllAppsRecyclerView workRecyclerView, + @NonNull RecyclerView.RecycledViewPool recycledViewPool) { + if (!ENABLE_ALL_APPS_RV_PREINFLATION.get()) { + return; + } + mainRecyclerView.setRecycledViewPool(recycledViewPool); + if (workRecyclerView != null) { + workRecyclerView.setRecycledViewPool(recycledViewPool); + } + if (ALL_APPS_GONE_VISIBILITY.get()) { + mainRecyclerView.updatePoolSize(); + } + } + private void replaceAppsRVContainer(boolean showTabs) { for (int i = AdapterHolder.MAIN; i <= AdapterHolder.WORK; i++) { AdapterHolder adapterHolder = mAH.get(i); diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 602d1a38a5..7edbeac71f 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps; +import static com.android.launcher3.config.FeatureFlags.ALL_APPS_GONE_VISIBILITY; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo; import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN; @@ -26,6 +27,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND; +import static com.android.launcher3.recyclerview.AllAppsRecyclerViewPoolKt.EXTRA_ICONS_COUNT; +import static com.android.launcher3.recyclerview.AllAppsRecyclerViewPoolKt.PREINFLATE_ICONS_ROW_COUNT; import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING; import android.content.Context; @@ -96,8 +99,18 @@ public class AllAppsRecyclerView extends FastScrollRecyclerView { int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1); pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1); + + // If all apps' hidden visibility is INVISIBLE, we will need to preinflate one page of + // all apps icons for smooth scrolling. + int maxPoolSizeForAppIcons = (approxRows + 1) * grid.numShownAllAppsColumns; + if (ALL_APPS_GONE_VISIBILITY.get()) { + // If all apps' hidden visibility is GONE, we need to increase prefinated icons number + // by [PREINFLATE_ICONS_ROW_COUNT] rows + [EXTRA_ICONS_COUNT] for fast opening all apps. + maxPoolSizeForAppIcons += + PREINFLATE_ICONS_ROW_COUNT * grid.numShownAllAppsColumns + EXTRA_ICONS_COUNT; + } pool.setMaxRecycledViews( - AllAppsGridAdapter.VIEW_TYPE_ICON, (approxRows + 1) * grid.numShownAllAppsColumns); + AllAppsGridAdapter.VIEW_TYPE_ICON, maxPoolSizeForAppIcons); } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 0d7b736cc8..c09a5b9d93 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -438,7 +438,8 @@ public class AllAppsTransitionController mAppsView = appsView; mAppsView.setScrimView(scrimView); - mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT); + mAppsViewAlpha = new MultiValueAlpha(mAppsView, APPS_VIEW_INDEX_COUNT, + FeatureFlags.ALL_APPS_GONE_VISIBILITY.get() ? View.GONE : View.INVISIBLE); mAppsViewAlpha.setUpdateVisibility(true); mAppsViewTranslationY = new MultiPropertyFactory<>( mAppsView, VIEW_TRANSLATE_Y, APPS_VIEW_INDEX_COUNT, Float::sum); diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java index 8dad1b4fa5..4382174530 100644 --- a/src/com/android/launcher3/anim/AlphaUpdateListener.java +++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java @@ -53,8 +53,18 @@ public class AlphaUpdateListener extends AnimatorListenerAdapter } public static void updateVisibility(View view) { - if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.INVISIBLE) { - view.setVisibility(View.INVISIBLE); + updateVisibility(view, View.INVISIBLE); + } + + /** + * Update view's visibility. + * + * @param view View that needs to update visibility. + * @param hiddenVisibility {@link View#GONE} or {@link View#INVISIBLE} + */ + public static void updateVisibility(View view, int hiddenVisibility) { + if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != hiddenVisibility) { + view.setVisibility(hiddenVisibility); } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.VISIBLE) { if (view instanceof ViewGroup) { diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index e3e14009ee..3989e7c4bb 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -395,10 +395,15 @@ public final class FeatureFlags { // TODO(Block 33): Clean up flags public static final BooleanFlag ENABLE_ALL_APPS_RV_PREINFLATION = getDebugFlag(288161355, - "ENABLE_ALL_APPS_RV_PREINFLATION", DISABLED, + "ENABLE_ALL_APPS_RV_PREINFLATION", ENABLED, "Enables preinflating all apps icons to avoid scrolling jank."); - // TODO(Block 34): Empty block + // TODO(Block 34): Clean up flags + public static final BooleanFlag ALL_APPS_GONE_VISIBILITY = getDebugFlag(291651514, + "ALL_APPS_GONE_VISIBILITY", ENABLED, + "Set all apps container view's hidden visibility to GONE instead of INVISIBLE."); + + // TODO(Block 35): Empty block public static class BooleanFlag { diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt index 26dde29d36..3c59c1d63f 100644 --- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt +++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt @@ -22,13 +22,14 @@ import androidx.recyclerview.widget.RecyclerView.RecycledViewPool import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.android.launcher3.BubbleTextView import com.android.launcher3.allapps.BaseAllAppsAdapter +import com.android.launcher3.config.FeatureFlags import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR import com.android.launcher3.views.ActivityContext import java.util.concurrent.Future -private const val PREINFLATE_ICONS_ROW_COUNT = 4 -private const val EXTRA_ICONS_COUNT = 2 +const val PREINFLATE_ICONS_ROW_COUNT = 4 +const val EXTRA_ICONS_COUNT = 2 /** * An [RecycledViewPool] that preinflates app icons ([ViewHolder] of [BubbleTextView]) of all apps @@ -81,11 +82,21 @@ class AllAppsRecyclerViewPool : RecycledViewPool() { * After testing on phone, foldable and tablet, we found [PREINFLATE_ICONS_ROW_COUNT] rows of * app icons plus [EXTRA_ICONS_COUNT] is the magic minimal count of app icons to preinflate to * suffice fast scrolling. + * + * Note that if [FeatureFlags.ALL_APPS_GONE_VISIBILITY] is enabled, we need to preinfate extra + * app icons in size of one all apps pages, so that opening all apps don't need to inflate app + * icons. */ fun getPreinflateCount(context: T): Int where T : Context, T : ActivityContext { - val targetPreinflateCount = + var targetPreinflateCount = PREINFLATE_ICONS_ROW_COUNT * context.deviceProfile.numShownAllAppsColumns + EXTRA_ICONS_COUNT + if (FeatureFlags.ALL_APPS_GONE_VISIBILITY.get()) { + val grid = ActivityContext.lookupContext(context).deviceProfile + val approxRows = + Math.ceil((grid.availableHeightPx / grid.allAppsIconSizePx).toDouble()).toInt() + targetPreinflateCount += (approxRows + 1) * grid.numShownAllAppsColumns + } val existingPreinflateCount = getRecycledViewCount(BaseAllAppsAdapter.VIEW_TYPE_ICON) return targetPreinflateCount - existingPreinflateCount } diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java index ac016a8595..a66a9d2d56 100644 --- a/src/com/android/launcher3/util/MultiValueAlpha.java +++ b/src/com/android/launcher3/util/MultiValueAlpha.java @@ -32,8 +32,15 @@ public class MultiValueAlpha extends MultiPropertyFactory { // Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values. private boolean mUpdateVisibility; + private final int mHiddenVisibility; + public MultiValueAlpha(View view, int size) { + this(view, size, View.INVISIBLE); + } + + public MultiValueAlpha(View view, int size, int hiddenVisibility) { super(view, VIEW_ALPHA, size, ALPHA_AGGREGATOR, 1f); + this.mHiddenVisibility = hiddenVisibility; } /** Sets whether we should update between INVISIBLE and VISIBLE based on alpha. */ @@ -45,7 +52,7 @@ public class MultiValueAlpha extends MultiPropertyFactory { protected void apply(float value) { super.apply(value); if (mUpdateVisibility) { - AlphaUpdateListener.updateVisibility(mTarget); + AlphaUpdateListener.updateVisibility(mTarget, mHiddenVisibility); } } } From d280c2f946b62cfd8df116716940d4f740c3090c Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Fri, 28 Jul 2023 21:19:26 +0000 Subject: [PATCH 10/14] Back open-close animation with an AnimatorSet. Subclasses can add more animations to the set to animate other components in sync with themselves. Test: Manual Bug: 289290185 Flag: No Change-Id: I96d9afe166e243bb0d2a9cecb8158e2e7d431a6c --- .../hybridhotseat/HotseatEduDialog.java | 7 +- .../allapps/TaskbarAllAppsSlideInView.java | 17 +-- .../launcher3/views/AbstractSlideInView.java | 100 +++++++++++------- .../launcher3/views/WidgetsEduView.java | 8 +- .../widget/AddItemWidgetsBottomSheet.java | 7 +- .../launcher3/widget/WidgetsBottomSheet.java | 7 +- .../widget/picker/WidgetsFullSheet.java | 21 ++-- 7 files changed, 78 insertions(+), 89 deletions(-) diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index bd4792358a..5691ecf9fd 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -19,7 +19,6 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN; -import android.animation.PropertyValuesHolder; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -30,7 +29,6 @@ import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; -import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; @@ -164,10 +162,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements I return; } mIsOpen = true; - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + setUpDefaultOpenAnimator().start(); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index 4f75ef5599..3fe7359573 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -17,9 +17,6 @@ package com.android.launcher3.taskbar.allapps; import static com.android.app.animation.Interpolators.EMPHASIZED; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; @@ -31,6 +28,7 @@ import android.window.OnBackInvokedDispatcher; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks; import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext; @@ -68,16 +66,9 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView mAllAppsCallbacks.onAllAppsTransitionEnd(true))); mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start(); } else { mTranslationShift = TRANSLATION_SHIFT_OPENED; diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java index de10fc518b..f69d299cf3 100644 --- a/src/com/android/launcher3/views/AbstractSlideInView.java +++ b/src/com/android/launcher3/views/AbstractSlideInView.java @@ -24,8 +24,7 @@ import static com.android.launcher3.LauncherAnimUtils.TABLET_BOTTOM_SHEET_SUCCES import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS; import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; @@ -51,6 +50,7 @@ import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatedFloat; +import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.SingleAxisSwipeDetector; @@ -82,15 +82,19 @@ public abstract class AbstractSlideInView protected static final float TRANSLATION_SHIFT_CLOSED = 1f; protected static final float TRANSLATION_SHIFT_OPENED = 0f; private static final float VIEW_NO_SCALE = 1f; + private static final int NO_DURATION = -1; protected final T mActivityContext; protected final SingleAxisSwipeDetector mSwipeDetector; - protected final ObjectAnimator mOpenCloseAnimator; + protected @NonNull AnimatorSet mOpenCloseAnimator; + private final ObjectAnimator mTranslationShiftAnimator; protected ViewGroup mContent; protected final View mColorScrim; - protected Interpolator mScrollInterpolator; + + private Interpolator mScrollInterpolator; + private long mScrollDuration; // range [0, 1], 0=> completely open, 1=> completely closed protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED; @@ -104,8 +108,8 @@ public abstract class AbstractSlideInView protected final AnimatedFloat mSlideInViewScale = new AnimatedFloat(this::onScaleProgressChanged, VIEW_NO_SCALE); protected boolean mIsBackProgressing; - @Nullable private Drawable mContentBackground; - @Nullable private View mContentBackgroundParentView; + private @Nullable Drawable mContentBackground; + private @Nullable View mContentBackgroundParentView; protected final ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() { @Override @@ -124,21 +128,52 @@ public abstract class AbstractSlideInView mActivityContext = ActivityContext.lookupContext(context); mScrollInterpolator = Interpolators.SCROLL_CUBIC; + mScrollDuration = NO_DURATION; mSwipeDetector = new SingleAxisSwipeDetector(context, this, SingleAxisSwipeDetector.VERTICAL); - mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSwipeDetector.finishedScrolling(); - announceAccessibilityChanges(); - } - }); + mOpenCloseAnimator = new AnimatorSet(); + mTranslationShiftAnimator = ObjectAnimator.ofPropertyValuesHolder(this); + int scrimColor = getScrimColor(context); mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null; } + /** + * Sets up a {@link #mOpenCloseAnimator} for opening with default parameters. + * + * @see #setUpOpenCloseAnimator(float, Interpolator) + */ + protected final AnimatorSet setUpDefaultOpenAnimator() { + return setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.FAST_OUT_SLOW_IN); + } + + /** + * Initializes a new {@link #mOpenCloseAnimator}. + *

+ * Subclasses should override this method if they want to add more {@code Animator} instances + * to the set. + * + * @param translationShift translation shift to animate to. + * @param translationShiftInterpolator interpolator for {@link #mTranslationShiftAnimator}. + * @return {@link #mOpenCloseAnimator} + */ + protected AnimatorSet setUpOpenCloseAnimator( + float translationShift, Interpolator translationShiftInterpolator) { + mOpenCloseAnimator = new AnimatorSet(); + mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(() -> { + mSwipeDetector.finishedScrolling(); + announceAccessibilityChanges(); + })); + + mTranslationShiftAnimator.setValues(PropertyValuesHolder.ofFloat( + TRANSLATION_SHIFT, translationShift)); + mTranslationShiftAnimator.setInterpolator(translationShiftInterpolator); + mOpenCloseAnimator.play(mTranslationShiftAnimator); + + return mOpenCloseAnimator; + } + protected void attachToContainer() { if (mColorScrim != null) { getPopupContainer().addView(mColorScrim); @@ -309,16 +344,13 @@ public abstract class AbstractSlideInView if ((mSwipeDetector.isFling(velocity) && velocity > 0) || mTranslationShift > successfulShiftThreshold) { mScrollInterpolator = scrollInterpolatorForVelocity(velocity); - mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration( - velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift)); + mScrollDuration = BaseSwipeDetector.calculateDuration( + velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift); close(true); } else { - mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat( - TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setDuration( - BaseSwipeDetector.calculateDuration(velocity, mTranslationShift)) - .setInterpolator(Interpolators.DECELERATE); - mOpenCloseAnimator.start(); + setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.DECELERATE) + .setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift)) + .start(); } } @@ -344,23 +376,19 @@ public abstract class AbstractSlideInView onCloseComplete(); return; } - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED)); - mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mOpenCloseAnimator.removeListener(this); - onCloseComplete(); - } - }); + + final Interpolator interpolator; + final long duration; if (mSwipeDetector.isIdleState()) { - mOpenCloseAnimator - .setDuration(defaultDuration) - .setInterpolator(getIdleInterpolator()); + interpolator = getIdleInterpolator(); + duration = defaultDuration; } else { - mOpenCloseAnimator.setInterpolator(mScrollInterpolator); + interpolator = mScrollInterpolator; + duration = mScrollDuration > NO_DURATION ? mScrollDuration : defaultDuration; } - mOpenCloseAnimator.start(); + setUpOpenCloseAnimator(TRANSLATION_SHIFT_CLOSED, interpolator) + .addListener(AnimatorListeners.forEndCallback(this::onCloseComplete)); + mOpenCloseAnimator.setDuration(duration).start(); } protected Interpolator getIdleInterpolator() { diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java index 918078178c..92e048bbf5 100644 --- a/src/com/android/launcher3/views/WidgetsEduView.java +++ b/src/com/android/launcher3/views/WidgetsEduView.java @@ -15,9 +15,6 @@ */ package com.android.launcher3.views; -import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; - -import android.animation.PropertyValuesHolder; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; @@ -123,10 +120,7 @@ public class WidgetsEduView extends AbstractSlideInView implements Ins return; } mIsOpen = true; - mOpenCloseAnimator.setValues( - PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); - mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN); - mOpenCloseAnimator.start(); + setUpDefaultOpenAnimator().start(); } /** Shows widget education dialog. */ diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java index 473abf1b1b..c6fc5fef85 100644 --- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java @@ -16,10 +16,8 @@ package com.android.launcher3.widget; -import static com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.Utilities.ATLEAST_R; -import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Insets; @@ -134,10 +132,7 @@ public class AddItemWidgetsBottomSheet extends AbstractSlideInView { - mOpenCloseAnimator.start(); + mOpenCloseAnimator + .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration) + .start(); mContent.animate().alpha(1).setDuration(FADE_IN_DURATION); }); } else { From c5ad40302cb4338f2b8e15059adf366b01ecbb37 Mon Sep 17 00:00:00 2001 From: randypfohl Date: Mon, 31 Jul 2023 16:32:31 -0700 Subject: [PATCH 11/14] Temporarily disabling test Test: ran test locally and ensured it was disabled. Bug: 293191790 Change-Id: I966c466785aa7011d798ae13a52ddb07a93e1ee5 --- tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index b67478f1c9..753d89d8b8 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -318,6 +318,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } @Test + @Ignore // b/293191790 @PortraitLandscape public void testWidgets() throws Exception { // Test opening widgets. From 189f115e8330210afc9542efeefe502e36795b80 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Fri, 28 Jul 2023 15:03:08 -0700 Subject: [PATCH 12/14] Detecting multiple view animation anomalies. Now detecting all anomalies that were detected during the test. This helps to avoid rerunning the test multiple times and adding anomalies to the ignore-list one by one. Generating a file with all detected anomalies and instructions how to suppress them. Flag: N/A Test: presubmit, local runs Bug: 286251603 Change-Id: I0c34d228f91976451b518fd44873218b80178d0e --- .../launcher3/util/rule/ViewCaptureRule.kt | 38 ++++++++-- .../AlphaJumpDetector.java | 18 +++-- .../ViewCaptureAnalyzer.java | 73 +++++++++++++------ 3 files changed, 95 insertions(+), 34 deletions(-) diff --git a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt index b4ad1f3542..f3f9b89b1c 100644 --- a/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt +++ b/tests/src/com/android/launcher3/util/rule/ViewCaptureRule.kt @@ -26,8 +26,13 @@ import com.android.app.viewcapture.data.ExportedData import com.android.launcher3.tapl.TestHelpers import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter import com.android.launcher3.util.viewcapture_analysis.ViewCaptureAnalyzer -import org.junit.Assert.assertTrue +import java.io.BufferedOutputStream +import java.io.FileOutputStream +import java.io.IOException +import java.io.OutputStreamWriter import java.util.function.Supplier +import org.junit.Assert.assertTrue +import org.junit.Assert.fail import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement @@ -81,7 +86,7 @@ class ViewCaptureRule(var alreadyOpenActivitySupplier: Supplier) : Te MAIN_EXECUTOR.execute { windowListenerCloseables.onEach(SafeCloseable::close) } } - analyzeViewCapture() + analyzeViewCapture(description) } private fun startCapturingExistingActivity( @@ -107,16 +112,39 @@ class ViewCaptureRule(var alreadyOpenActivitySupplier: Supplier) : Te } } - private fun analyzeViewCapture() { + private fun analyzeViewCapture(description: Description) { // OOP tests don't produce ViewCapture data if (!TestHelpers.isInLauncherProcess()) return - ViewCaptureAnalyzer.assertNoAnomalies(viewCaptureData) - var frameCount = 0 for (i in 0 until viewCaptureData!!.windowDataCount) { frameCount += viewCaptureData!!.getWindowData(i).frameDataCount } assertTrue("Empty ViewCapture data", frameCount > 0) + + val anomalies: Map = ViewCaptureAnalyzer.getAnomalies(viewCaptureData) + if (!anomalies.isEmpty()) { + val diagFile = FailureWatcher.diagFile(description, "ViewAnomalies", "txt") + try { + OutputStreamWriter(BufferedOutputStream(FileOutputStream(diagFile))).use { writer -> + writer.write("View animation anomalies detected.\r\n") + writer.write( + "To suppress an anomaly for a view, add its full path to the PATHS_TO_IGNORE list in the corresponding AnomalyDetector.\r\n" + ) + writer.write("List of views with animation anomalies:\r\n") + + for ((viewPath, message) in anomalies) { + writer.write("View: $viewPath\r\n $message\r\n") + } + } + } catch (ex: IOException) { + throw RuntimeException(ex) + } + + val (viewPath, message) = anomalies.entries.first() + fail( + "${anomalies.size} view(s) had animation anomalies during the test, including view: $viewPath: $message\r\nSee ${diagFile.name} for details." + ) + } } } diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java index cb404b966c..f67f341c94 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/AlphaJumpDetector.java @@ -212,24 +212,26 @@ final class AlphaJumpDetector extends AnomalyDetector { } @Override - void detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN) { + String detectAnomalies(AnalysisNode oldInfo, AnalysisNode newInfo, int frameN) { // If the view was previously seen, proceed with analysis only if it was present in the // view hierarchy in the previous frame. - if (oldInfo != null && oldInfo.frameN != frameN) return; + if (oldInfo != null && oldInfo.frameN != frameN) return null; final AnalysisNode latestInfo = newInfo != null ? newInfo : oldInfo; - if (getNodeData(latestInfo).ignoreAlphaJumps) return; + final NodeData nodeData = getNodeData(latestInfo); + if (nodeData.ignoreAlphaJumps) return null; final float oldAlpha = oldInfo != null ? oldInfo.alpha : 0; final float newAlpha = newInfo != null ? newInfo.alpha : 0; final float alphaDeltaAbs = Math.abs(newAlpha - oldAlpha); if (alphaDeltaAbs >= ALPHA_JUMP_THRESHOLD) { - throw new AssertionError( - String.format( - "Alpha jump detected in ViewCapture data: alpha change: %s (%s -> %s)" - + ", threshold: %s, view: %s", - alphaDeltaAbs, oldAlpha, newAlpha, ALPHA_JUMP_THRESHOLD, latestInfo)); + nodeData.ignoreAlphaJumps = true; // No need to report alpha jump in children. + return String.format( + "Alpha jump detected in ViewCapture data: alpha change: %s (%s -> %s)" + + ", threshold: %s, %s", // ----------- no need to include view? + alphaDeltaAbs, oldAlpha, newAlpha, ALPHA_JUMP_THRESHOLD, latestInfo); } + return null; } } diff --git a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java index 2cf38437f5..949c5368ae 100644 --- a/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java +++ b/tests/src/com/android/launcher3/util/viewcapture_analysis/ViewCaptureAnalyzer.java @@ -61,8 +61,9 @@ public class ViewCaptureAnalyzer { * the view is not present in the 'currentFrame', but was present in earlier * frames. * @param frameN number of the current frame. + * @return Anomaly diagnostic message if an anomaly has been detected; null otherwise. */ - abstract void detectAnomalies( + abstract String detectAnomalies( @Nullable AnalysisNode oldInfo, @Nullable AnalysisNode newInfo, int frameN); } @@ -101,25 +102,31 @@ public class ViewCaptureAnalyzer { @Override public String toString() { - return String.format("window coordinates: (%s, %s), class path from the root: %s", - left, top, diagPathFromRoot(this)); + return String.format("view window coordinates: (%s, %s)", left, top); } } /** - * Scans a view capture record and throws an error if an anomaly is found. + * Scans a view capture record and searches for view animation anomalies. Can find anomalies for + * multiple views. + * Returns a map from the view path to the anomaly message for the view. Non-empty map means + * that anomalies were detected. */ - public static void assertNoAnomalies(ExportedData viewCaptureData) { + public static Map getAnomalies(ExportedData viewCaptureData) { + final Map anomalies = new HashMap<>(); + final int scrimClassIndex = viewCaptureData.getClassnameList().indexOf(SCRIM_VIEW_CLASS); final int windowDataCount = viewCaptureData.getWindowDataCount(); for (int i = 0; i < windowDataCount; ++i) { - analyzeWindowData(viewCaptureData, viewCaptureData.getWindowData(i), scrimClassIndex); + analyzeWindowData( + viewCaptureData, viewCaptureData.getWindowData(i), scrimClassIndex, anomalies); } + return anomalies; } private static void analyzeWindowData(ExportedData viewCaptureData, WindowData windowData, - int scrimClassIndex) { + int scrimClassIndex, Map anomalies) { // View hash code => Last seen node with this hash code. // The view is added when we analyze the first frame where it's visible. // After that, it gets updated for every frame where it's visible. @@ -128,12 +135,13 @@ public class ViewCaptureAnalyzer { for (int frameN = 0; frameN < windowData.getFrameDataCount(); ++frameN) { analyzeFrame(frameN, windowData.getFrameData(frameN), viewCaptureData, lastSeenNodes, - scrimClassIndex); + scrimClassIndex, anomalies); } } private static void analyzeFrame(int frameN, FrameData frame, ExportedData viewCaptureData, - Map lastSeenNodes, int scrimClassIndex) { + Map lastSeenNodes, int scrimClassIndex, + Map anomalies) { // Analyze the node tree starting from the root. analyzeView( frame.getNode(), @@ -143,7 +151,8 @@ public class ViewCaptureAnalyzer { /* topShift = */ 0, viewCaptureData, lastSeenNodes, - scrimClassIndex); + scrimClassIndex, + anomalies); // Analyze transitions when a view visible in the last frame become invisible in the // current one. @@ -151,10 +160,14 @@ public class ViewCaptureAnalyzer { if (info.frameN == frameN - 1) { if (!info.viewCaptureNode.getWillNotDraw()) { Arrays.stream(ANOMALY_DETECTORS).forEach( - detector -> detector.detectAnomalies( - /* oldInfo = */ info, - /* newInfo = */ null, - frameN)); + detector -> + detectAnomaly( + detector, + frameN, + /* oldInfo = */ info, + /* newInfo = */ null, + anomalies) + ); } } } @@ -162,7 +175,8 @@ public class ViewCaptureAnalyzer { private static void analyzeView(ViewNode viewCaptureNode, AnalysisNode parent, int frameN, float leftShift, float topShift, ExportedData viewCaptureData, - Map lastSeenNodes, int scrimClassIndex) { + Map lastSeenNodes, int scrimClassIndex, + Map anomalies) { // Skip analysis of invisible views final float parentAlpha = parent != null ? parent.alpha : 1; final float alpha = getVisibleAlpha(viewCaptureNode, parentAlpha); @@ -205,7 +219,10 @@ public class ViewCaptureAnalyzer { final AnalysisNode oldAnalysisNode = lastSeenNodes.get(hashcode); // may be null if (frameN != 0 && !viewCaptureNode.getWillNotDraw()) { Arrays.stream(ANOMALY_DETECTORS).forEach( - detector -> detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN)); + detector -> + detectAnomaly(detector, frameN, oldAnalysisNode, newAnalysisNode, + anomalies) + ); } lastSeenNodes.put(hashcode, newAnalysisNode); @@ -220,8 +237,20 @@ public class ViewCaptureAnalyzer { if (child.getClassnameIndex() == scrimClassIndex) break; analyzeView(child, newAnalysisNode, frameN, leftShiftForChildren, topShiftForChildren, - viewCaptureData, lastSeenNodes, - scrimClassIndex); + viewCaptureData, lastSeenNodes, scrimClassIndex, anomalies); + } + } + + private static void detectAnomaly(AnomalyDetector detector, int frameN, + AnalysisNode oldAnalysisNode, AnalysisNode newAnalysisNode, + Map anomalies) { + final String maybeAnomaly = + detector.detectAnomalies(oldAnalysisNode, newAnalysisNode, frameN); + if (maybeAnomaly != null) { + final String viewDiagPath = diagPathFromRoot(newAnalysisNode); + if (!anomalies.containsKey(viewDiagPath)) { + anomalies.put(viewDiagPath, maybeAnomaly); + } } } @@ -235,9 +264,11 @@ public class ViewCaptureAnalyzer { return className.substring(className.lastIndexOf(".") + 1); } - private static String diagPathFromRoot(AnalysisNode nodeBox) { - final StringBuilder path = new StringBuilder(nodeBox.nodeIdentity); - for (AnalysisNode ancestor = nodeBox.parent; ancestor != null; ancestor = ancestor.parent) { + private static String diagPathFromRoot(AnalysisNode analysisNode) { + final StringBuilder path = new StringBuilder(analysisNode.nodeIdentity); + for (AnalysisNode ancestor = analysisNode.parent; + ancestor != null; + ancestor = ancestor.parent) { path.insert(0, ancestor.nodeIdentity + "|"); } return path.toString(); From a3d564ce341bf6db3d69895c360f8436c77cc1ef Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Mon, 31 Jul 2023 13:57:47 +0100 Subject: [PATCH 13/14] Set taskbar to fullscreen for tooltip only once it is shown. Fix: 293490434 Test: TaskbarHoverToolTipControllerTest Change-Id: Iba59ce241933a52e7f49036188660bef93ea6dcb --- .../taskbar/TaskbarHoverToolTipController.java | 6 ++++-- .../taskbar/TaskbarHoverToolTipControllerTest.java | 13 ++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java index 363f915e95..c3ec1e5ad0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java @@ -35,6 +35,8 @@ import android.view.ContextThemeWrapper; import android.view.MotionEvent; import android.view.View; +import androidx.annotation.VisibleForTesting; + import com.android.app.animation.Interpolators; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; @@ -49,7 +51,7 @@ import com.android.launcher3.views.ArrowTipView; */ public class TaskbarHoverToolTipController implements View.OnHoverListener { - private static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400; + @VisibleForTesting protected static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400; private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 300; private static final int HOVER_TOOL_TIP_EXIT_DURATION = 150; @@ -145,7 +147,6 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener { } private void startRevealHoverToolTip() { - mActivity.setTaskbarWindowFullscreen(true); mHoverToolTipHandler.postDelayed(mRevealHoverToolTipRunnable, HOVER_TOOL_TIP_REVEAL_START_DELAY); } @@ -157,6 +158,7 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener { if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) { return; } + mActivity.setTaskbarWindowFullscreen(true); Rect iconViewBounds = Utilities.getViewBounds(mHoverView); mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(), mTaskbarView.getTop(), /* shouldAutoClose= */ false); diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java index 82849be26e..6c0d44da6c 100644 --- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java +++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarHoverToolTipControllerTest.java @@ -18,6 +18,7 @@ package com.android.launcher3.taskbar; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS; +import static com.android.launcher3.taskbar.TaskbarHoverToolTipController.HOVER_TOOL_TIP_REVEAL_START_DELAY; import static com.google.common.truth.Truth.assertThat; @@ -26,6 +27,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,7 +58,7 @@ import org.mockito.stubbing.Answer; */ @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { private TaskbarHoverToolTipController mTaskbarHoverToolTipController; @@ -126,8 +128,10 @@ public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { boolean hoverHandled = mTaskbarHoverToolTipController.onHover(mHoverBubbleTextView, mMotionEvent); - waitForIdleSync(); + // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run + verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true); + waitForIdleSync(); assertThat(hoverHandled).isTrue(); verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true); @@ -155,8 +159,10 @@ public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { boolean hoverHandled = mTaskbarHoverToolTipController.onHover(mHoverFolderIcon, mMotionEvent); - waitForIdleSync(); + // Verify fullscreen is not set until the delayed runnable to reveal the tooltip has run + verify(taskbarActivityContext, never()).setTaskbarWindowFullscreen(true); + waitForIdleSync(); assertThat(hoverHandled).isTrue(); verify(taskbarActivityContext).setAutohideSuspendFlag(FLAG_AUTOHIDE_SUSPEND_HOVERING_ICONS, true); @@ -216,6 +222,7 @@ public class TaskbarHoverToolTipControllerTest extends TaskbarBaseTestCase { } private void waitForIdleSync() { + mTestableLooper.moveTimeForward(HOVER_TOOL_TIP_REVEAL_START_DELAY + 1); mTestableLooper.processAllMessages(); } } From 5225e83d71189a3e161e7b5e1fb78a9c896701ca Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Tue, 1 Aug 2023 16:32:15 +0100 Subject: [PATCH 14/14] Skip iconScale logic in responsive grid Fix: 289198168 Flag: ENABLE_RESPONSIVE_WORKSPACE Test: DeviceProfileResponsiveDumpTest, DeviceProfileResponsiveAlternativeDisplaysDumpTest Change-Id: Iefe9ab3986a6d50ac920ba7bf417c0a8f254d878 --- src/com/android/launcher3/DeviceProfile.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 28e0cf2de7..9a3480b6a8 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -886,6 +886,10 @@ public class DeviceProfile { updateIconSize(1f, res); updateWorkspacePadding(); + if (mIsResponsiveGrid) { + return 0; + } + // Check to see if the icons fit within the available height. float usedHeight = getCellLayoutHeightSpecification(); final int maxHeight = getCellLayoutHeight();