Merge "Folder icon polish."
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index dfa17d6..c0e0862 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -36,18 +36,27 @@
         android:layout_height="wrap_content"
         android:layout_gravity="bottom" >
 
+        <FrameLayout
+            android:id="@+id/start_contextual_buttons"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
+            android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
+            android:gravity="center_vertical"
+            android:layout_gravity="start"/>
+
         <LinearLayout
-            android:id="@+id/start_nav_buttons"
+            android:id="@+id/end_nav_buttons"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:orientation="horizontal"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
             android:paddingRight="@dimen/taskbar_nav_buttons_spacing"
             android:gravity="center_vertical"
-            android:layout_gravity="start"/>
+            android:layout_gravity="end"/>
 
         <FrameLayout
-            android:id="@+id/end_nav_buttons"
+            android:id="@+id/end_contextual_buttons"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:paddingLeft="@dimen/taskbar_nav_buttons_spacing"
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index 82582ee..370fb8e 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -100,9 +100,12 @@
                 }
             };
 
-    private final Consumer<Boolean> mCrossWindowBlurListener = (enabled) -> {
-        mCrossWindowBlursEnabled = enabled;
-        dispatchTransactionSurface();
+    private final Consumer<Boolean> mCrossWindowBlurListener = new Consumer<Boolean>() {
+        @Override
+        public void accept(Boolean enabled) {
+            mCrossWindowBlursEnabled = enabled;
+            dispatchTransactionSurface(mDepth);
+        }
     };
 
     private final Launcher mLauncher;
@@ -189,14 +192,14 @@
         if (mSurface != surface) {
             mSurface = surface;
             if (surface != null) {
-                dispatchTransactionSurface();
+                dispatchTransactionSurface(mDepth);
             }
         }
     }
 
     @Override
     public void setState(LauncherState toState) {
-        if (mIgnoreStateChangesDuringMultiWindowAnimation) {
+        if (mSurface == null || mIgnoreStateChangesDuringMultiWindowAnimation) {
             return;
         }
 
@@ -204,7 +207,7 @@
         if (Float.compare(mDepth, toDepth) != 0) {
             setDepth(toDepth);
         } else if (toState == LauncherState.OVERVIEW) {
-            dispatchTransactionSurface();
+            dispatchTransactionSurface(mDepth);
         }
     }
 
@@ -243,31 +246,36 @@
         if (Float.compare(mDepth, depthF) == 0) {
             return;
         }
-        mDepth = depthF;
-        dispatchTransactionSurface();
+        if (dispatchTransactionSurface(depthF)) {
+            mDepth = depthF;
+        }
     }
 
-    private void dispatchTransactionSurface() {
+    private boolean dispatchTransactionSurface(float depth) {
         boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
+        if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
+            return false;
+        }
         ensureDependencies();
         IBinder windowToken = mLauncher.getRootView().getWindowToken();
         if (windowToken != null) {
-            mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
+            mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
         }
 
-        if (supportsBlur && (mSurface != null && mSurface.isValid())) {
+        if (supportsBlur) {
             // We cannot mark the window as opaque in overview because there will be an app window
             // below the launcher layer, and we need to draw it -- without blurs.
             boolean isOverview = mLauncher.isInState(LauncherState.OVERVIEW);
             boolean opaque = mLauncher.getScrimView().isFullyOpaque() && !isOverview;
 
             int blur = opaque || isOverview || !mCrossWindowBlursEnabled
-                    || mBlurDisabledForAppLaunch ? 0 : (int) (mDepth * mMaxBlurRadius);
+                    || mBlurDisabledForAppLaunch ? 0 : (int) (depth * mMaxBlurRadius);
             new SurfaceControl.Transaction()
                     .setBackgroundBlurRadius(mSurface, blur)
                     .setOpaque(mSurface, opaque)
                     .apply();
         }
+        return true;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 1edeba0..357dc7b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -79,8 +79,10 @@
 
     private final TaskbarActivityContext mContext;
     private final FrameLayout mNavButtonsView;
-    private final ViewGroup mStartContainer;
-    private final ViewGroup mEndContainer;
+    private final ViewGroup mNavButtonContainer;
+    // Used for IME+A11Y buttons
+    private final ViewGroup mEndContextualContainer;
+    private final ViewGroup mStartContextualContainer;
 
     // Initialized in init.
     private TaskbarControllers mControllers;
@@ -91,8 +93,9 @@
     public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
         mContext = context;
         mNavButtonsView = navButtonsView;
-        mStartContainer = mNavButtonsView.findViewById(R.id.start_nav_buttons);
-        mEndContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
+        mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
+        mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
+        mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
     }
 
     /**
@@ -114,21 +117,22 @@
 
         // IME switcher
         View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH,
-                mEndContainer, mControllers.navButtonController, R.id.ime_switcher);
+                mEndContextualContainer, mControllers.navButtonController, R.id.ime_switcher);
         mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
                 flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
                         && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)
                         && ((flags & FLAG_A11Y_VISIBLE) == 0)));
 
-        mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
-                mStartContainer, mControllers.navButtonController, R.id.back);
+        View imeDownButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
+                mStartContextualContainer, mControllers.navButtonController, R.id.back);
+        imeDownButton.setRotation(Utilities.isRtl(mContext.getResources()) ? 90 : -90);
         // Rotate when Ime visible
-        mPropertyHolders.add(new StatePropertyHolder(mBackButton,
-                flags -> (flags & FLAG_IME_VISIBLE) == 0, View.ROTATION, 0,
-                Utilities.isRtl(mContext.getResources()) ? 90 : -90));
+        mPropertyHolders.add(new StatePropertyHolder(imeDownButton,
+                flags -> (flags & FLAG_IME_VISIBLE) != 0));
 
         if (mContext.isThreeButtonNav()) {
-            initButtons(mStartContainer, mEndContainer, mControllers.navButtonController);
+            initButtons(mNavButtonContainer, mEndContextualContainer,
+                    mControllers.navButtonController);
 
             // Animate taskbar background when IME shows
             mPropertyHolders.add(new StatePropertyHolder(
@@ -142,21 +146,18 @@
 
             // Rotation button
             RotationButton rotationButton = new RotationButtonImpl(
-                    addButton(mEndContainer, R.id.rotate_suggestion));
+                    addButton(mEndContextualContainer, R.id.rotate_suggestion));
             rotationButton.hide();
             mControllers.rotationButtonController.setRotationButton(rotationButton);
         } else {
             mControllers.rotationButtonController.setRotationButton(new RotationButton() {});
-            // Show when IME is visible
-            mPropertyHolders.add(new StatePropertyHolder(mBackButton,
-                    flags -> (flags & FLAG_IME_VISIBLE) != 0));
         }
 
         applyState();
         mPropertyHolders.forEach(StatePropertyHolder::endAnimation);
     }
 
-    private void initButtons(ViewGroup startContainer, ViewGroup endContainer,
+    private void initButtons(ViewGroup navContainer, ViewGroup endContainer,
             TaskbarNavButtonController navButtonController) {
 
         // Hide when keyguard is showing, show when bouncer is showing
@@ -164,14 +165,19 @@
                 flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 ||
                         (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0));
 
+        mBackButton = addButton(R.drawable.ic_sysbar_back, BUTTON_BACK,
+                mNavButtonContainer, mControllers.navButtonController, R.id.back);
+        mPropertyHolders.add(new StatePropertyHolder(mBackButton,
+                flags -> (flags & FLAG_IME_VISIBLE) == 0));
+
         // home and recents buttons
-        View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, startContainer,
+        View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
                 navButtonController, R.id.home);
         mPropertyHolders.add(new StatePropertyHolder(homeButton,
                 flags -> (flags & FLAG_IME_VISIBLE) == 0 &&
                         (flags & FLAG_KEYGUARD_VISIBLE) == 0));
         View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
-                startContainer, navButtonController, R.id.recent_apps);
+                navContainer, navButtonController, R.id.recent_apps);
         mPropertyHolders.add(new StatePropertyHolder(recentsButton,
                 flags -> (flags & FLAG_IME_VISIBLE) == 0 &&
                         (flags & FLAG_KEYGUARD_VISIBLE) == 0));
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 6f53f2b..a4a92f7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -194,24 +194,30 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         int count = getChildCount();
         int spaceNeeded = count * (mItemMarginLeftRight * 2 + mIconTouchSize);
-        int iconStart = (right - left - spaceNeeded) / 2;
-        int startOffset = ApiWrapper.getHotseatStartOffset(getContext());
-        if (startOffset > iconStart) {
-            int diff = startOffset - iconStart;
-            iconStart = isLayoutRtl() ? (iconStart - diff) : iconStart + diff;
+        int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
+        boolean layoutRtl = isLayoutRtl();
+        int iconEnd = right - (right - left - spaceNeeded) / 2;
+        boolean needMoreSpaceForNav = layoutRtl ?
+                navSpaceNeeded > (iconEnd - spaceNeeded) :
+                iconEnd > (right - navSpaceNeeded);
+        if (needMoreSpaceForNav) {
+            int offset = layoutRtl ?
+                    navSpaceNeeded - (iconEnd - spaceNeeded) :
+                    (right - navSpaceNeeded) - iconEnd;
+            iconEnd += offset;
         }
         // Layout the children
-        mIconLayoutBounds.left = iconStart;
+        mIconLayoutBounds.right = iconEnd;
         mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
         mIconLayoutBounds.bottom = mIconLayoutBounds.top + mIconTouchSize;
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            iconStart += mItemMarginLeftRight;
-            int iconEnd = iconStart + mIconTouchSize;
+        for (int i = count; i > 0; i--) {
+            View child = getChildAt(i - 1);
+            iconEnd -= mItemMarginLeftRight;
+            int iconStart = iconEnd - mIconTouchSize;
             child.layout(iconStart, mIconLayoutBounds.top, iconEnd, mIconLayoutBounds.bottom);
-            iconStart = iconEnd + mItemMarginLeftRight;
+            iconEnd = iconStart - mItemMarginLeftRight;
         }
-        mIconLayoutBounds.right = iconStart;
+        mIconLayoutBounds.left = iconEnd;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index a595f54..85943b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -44,9 +44,9 @@
     }
 
     /**
-     * Returns the minimum space that should be left empty at the start of hotseat
+     * Returns the minimum space that should be left empty at the end of hotseat
      */
-    public static int getHotseatStartOffset(Context context) {
+    public static int getHotseatEndOffset(Context context) {
         if (SysUINavigationMode.INSTANCE.get(context).getMode() == Mode.THREE_BUTTONS) {
             Resources res = context.getResources();
             return 2 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_spacing)
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 20d7eb1..7bb0b94 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -67,6 +67,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.BaseDraggingActivity;
@@ -301,17 +302,26 @@
 
     private static boolean sConnected = false;
     private static boolean sIsInitialized = false;
+    private static TouchInteractionService sInstance;
     private RotationTouchHelper mRotationTouchHelper;
 
     public static boolean isConnected() {
         return sConnected;
     }
 
-
     public static boolean isInitialized() {
         return sIsInitialized;
     }
 
+    @VisibleForTesting
+    @Nullable
+    public static TaskbarManager getTaskbarManagerForTesting() {
+        if (sInstance == null) {
+            return null;
+        }
+        return sInstance.mTaskbarManager;
+    }
+
     private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
             this::createLauncherSwipeHandler;
     private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
@@ -355,6 +365,7 @@
         mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
         ProtoTracer.INSTANCE.get(this).add(this);
         sConnected = true;
+        sInstance = this;
     }
 
     private void disposeEventHandlers() {
@@ -512,6 +523,7 @@
 
         mTaskbarManager.destroy();
         sConnected = false;
+        sInstance = null;
         super.onDestroy();
     }
 
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index f44a812..fe1bb2e 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -25,7 +25,12 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.Launcher;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarDragLayer;
+import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.launcher3.taskbar.TaskbarView;
 import com.android.launcher3.ui.TaplTestsLauncher3;
+import com.android.launcher3.ui.TestViewHelpers;
 import com.android.launcher3.util.RaceConditionReproducer;
 import com.android.quickstep.NavigationModeSwitchRule.Mode;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -85,7 +90,7 @@
     public void testStressPressHome() {
         for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
             // Destroy Launcher activity.
-            closeLauncherActivity();
+            destroyLauncherActivityAndWaitForTaskbarVisible();
 
             // The test action.
             mLauncher.pressHome();
@@ -97,12 +102,41 @@
     public void testStressSwipeToOverview() {
         for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
             // Destroy Launcher activity.
-            closeLauncherActivity();
+            destroyLauncherActivityAndWaitForTaskbarVisible();
 
             // The test action.
             mLauncher.getBackground().switchToOverview();
         }
-        closeLauncherActivity();
+        destroyLauncherActivityAndWaitForTaskbarVisible();
         mLauncher.pressHome();
     }
+
+    private void destroyLauncherActivityAndWaitForTaskbarVisible() {
+        closeLauncherActivity();
+
+        // After Launcher is destroyed, calculator app started in setup() will be launched, wait for
+        // taskbar to settle before further interaction if it's a tablet.
+        if (!mLauncher.isTablet()) {
+            return;
+        }
+
+        waitForLauncherCondition(
+                "Taskbar not yet visible", launcher -> {
+                    TaskbarManager taskbarManager =
+                            TouchInteractionService.getTaskbarManagerForTesting();
+                    if (taskbarManager == null) {
+                        return false;
+                    }
+
+                    TaskbarActivityContext taskbarActivityContext =
+                            taskbarManager.getCurrentActivityContext();
+                    if (taskbarActivityContext == null) {
+                        return false;
+                    }
+
+                    TaskbarDragLayer taskbarDragLayer = taskbarActivityContext.getDragLayer();
+                    return TestViewHelpers.findChildView(taskbarDragLayer,
+                            view -> view instanceof TaskbarView && view.isVisibleToUser()) != null;
+                }, DEFAULT_UI_TIMEOUT);
+    }
 }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7cf8826..222c7d6 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -780,7 +780,7 @@
             int taskbarOffset = getTaskbarOffsetY();
             int hotseatTopDiff = hotseatHeight - taskbarSize - taskbarOffset;
 
-            int startOffset = ApiWrapper.getHotseatStartOffset(context);
+            int endOffset = ApiWrapper.getHotseatEndOffset(context);
             int requiredWidth = iconSizePx * numShownHotseatIcons;
 
             Resources res = context.getResources();
@@ -788,16 +788,16 @@
             float taskbarIconSpacing = 2 * res.getDimension(R.dimen.taskbar_icon_spacing);
             int maxSize = (int) (requiredWidth
                     * (taskbarIconSize + taskbarIconSpacing) / taskbarIconSize);
-            int hotseatSize = Math.min(maxSize, availableWidthPx - startOffset);
+            int hotseatSize = Math.min(maxSize, availableWidthPx - endOffset);
             int sideSpacing = (availableWidthPx - hotseatSize) / 2;
             mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset);
 
-            if (startOffset > sideSpacing) {
+            if (endOffset > sideSpacing) {
                 int diff = Utilities.isRtl(context.getResources())
-                        ? sideSpacing - startOffset
-                        : startOffset - sideSpacing;
-                mHotseatPadding.left += diff;
-                mHotseatPadding.right -= diff;
+                        ? sideSpacing - endOffset
+                        : endOffset - sideSpacing;
+                mHotseatPadding.left -= diff;
+                mHotseatPadding.right += diff;
             }
         } else {
             // We want the edges of the hotseat to line up with the edges of the workspace, but the
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index c606861..cc90e6c 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -39,9 +39,9 @@
     }
 
     /**
-     * Returns the minimum space that should be left empty at the start of hotseat
+     * Returns the minimum space that should be left empty at the end of hotseat
      */
-    public static int getHotseatStartOffset(Context context) {
+    public static int getHotseatEndOffset(Context context) {
         return 0;
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 9da3e71..7f9f1b6 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -745,7 +745,6 @@
                     dumpViewHierarchy();
                     action = "swiping up to home";
 
-                    final boolean launcherIsVisible = isLauncherVisible();
                     swipeToState(
                             displaySize.x / 2, displaySize.y - 1,
                             displaySize.x / 2, 0,
@@ -753,9 +752,6 @@
                             launcherWasVisible
                                     ? GestureScope.INSIDE_TO_OUTSIDE
                                     : GestureScope.OUTSIDE_WITH_PILFER);
-                    // b/193653850: launcherWasVisible is a flaky indicator.
-                    log("launcherWasVisible: " + launcherWasVisible + ", launcherIsVisible: "
-                            + launcherIsVisible);
                 }
             } else {
                 log("Hierarchy before clicking home:");