Snap for 7406169 from 0bb96c91682adbab53c7b0f6b3dabef6c326047a to sc-release

Change-Id: I0f17e42076c370e8fd2155f7293203c626b411c5
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4720f55..c13225a 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -171,7 +171,7 @@
     }
 
     @Override
-    protected void onUiChangedWhileSleeping() {
+    public void onUiChangedWhileSleeping() {
         // Remove the snapshot because the content view may have obvious changes.
         UI_HELPER_EXECUTOR.execute(
                 () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
diff --git a/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java
deleted file mode 100644
index 98bf483..0000000
--- a/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * Copyright (C) 2019 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.appprediction;
-
-import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-
-import android.os.UserManager;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.views.ArrowTipView;
-
-/**
- * ArrowTip helper aligned just above prediction apps, shown to users that enter all apps for the
- * first time.
- */
-public class AllAppsTipView {
-
-    private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
-
-    private static boolean showAllAppsTipIfNecessary(Launcher launcher) {
-        FloatingHeaderView floatingHeaderView = launcher.getAppsView().getFloatingHeaderView();
-        if (!floatingHeaderView.hasVisibleContent()
-                || AbstractFloatingView.getOpenView(launcher,
-                TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE) != null
-                || !launcher.isInState(ALL_APPS)
-                || hasSeenAllAppsTip(launcher)
-                || launcher.getSystemService(UserManager.class).isDemoUser()
-                || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
-            return false;
-        }
-
-        int[] coords = new int[2];
-        floatingHeaderView.findFixedRowByType(PredictionRowView.class).getLocationOnScreen(coords);
-        ArrowTipView arrowTipView = new ArrowTipView(launcher).setOnClosedCallback(() -> {
-            launcher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
-            // TODO: add log to WW
-        });
-        arrowTipView.show(launcher.getString(R.string.all_apps_prediction_tip), coords[1]);
-
-        return true;
-    }
-
-    private static boolean hasSeenAllAppsTip(Launcher launcher) {
-        return launcher.getSharedPrefs().getBoolean(ALL_APPS_TIP_SEEN, false);
-    }
-
-    public static void scheduleShowIfNeeded(Launcher launcher) {
-        if (!hasSeenAllAppsTip(launcher)) {
-            launcher.getStateManager().addStateListener(new StateListener<LauncherState>() {
-                @Override
-                public void onStateTransitionComplete(LauncherState finalState) {
-                    if (finalState == ALL_APPS) {
-                        if (showAllAppsTipIfNecessary(launcher)) {
-                            launcher.getStateManager().removeStateListener(this);
-                        }
-                    }
-                }
-            });
-        }
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 6d5975f..de04082 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -88,7 +88,6 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        AllAppsTipView.scheduleShowIfNeeded(mLauncher);
     }
 
     public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 005e9b5..92e8268 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -82,6 +82,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.statemanager.BaseState;
@@ -1214,20 +1215,22 @@
                 && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
             swipePipToHomeAnimator.setFromRotation(mTaskViewSimulator, windowRotation);
         }
+        AnimatorPlaybackController activityAnimationToHome =
+                homeAnimFactory.createActivityAnimationToHome();
         swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
             private boolean mHasAnimationEnded;
             @Override
             public void onAnimationStart(Animator animation) {
                 if (mHasAnimationEnded) return;
-                // Ensure Launcher ends in NORMAL state, we intentionally skip other callbacks
-                // since they are not relevant in this swipe-pip-to-home case.
-                homeAnimFactory.createActivityAnimationToHome().dispatchOnStart();
+                // Ensure Launcher ends in NORMAL state
+                activityAnimationToHome.dispatchOnStart();
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (mHasAnimationEnded) return;
                 mHasAnimationEnded = true;
+                activityAnimationToHome.getAnimationPlayer().end();
                 if (mRecentsAnimationController == null) {
                     // If the recents animation is interrupted, we still end the running
                     // animation (not canceled) so this is still called. In that case, we can
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index e821e06..fa37901 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -561,7 +561,7 @@
             return false;
         }
 
-        if (mIsOneHandedModeEnabled || mIsSwipeToNotificationEnabled) {
+        if (mIsOneHandedModeEnabled) {
             final Info displayInfo = mDisplayController.getInfo();
             return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
                 && displayInfo.rotation != Surface.ROTATION_90
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 7ef6a4a..d040904 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -40,6 +40,8 @@
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
+import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
+import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
 import com.android.wm.shell.pip.IPipAnimationListener;
@@ -61,6 +63,7 @@
 
     private ISystemUiProxy mSystemUiProxy;
     private IPip mPip;
+    private ISmartspaceTransitionController mSmartspaceTransitionController;
     private ISplitScreen mSplitScreen;
     private IOneHanded mOneHanded;
     private IShellTransitions mShellTransitions;
@@ -74,6 +77,7 @@
     private IPipAnimationListener mPendingPipAnimationListener;
     private ISplitScreenListener mPendingSplitScreenListener;
     private IStartingWindowListener mPendingStartingWindowListener;
+    private ISmartspaceCallback mPendingSmartspaceCallback;
 
     // Used to dedupe calls to SystemUI
     private int mLastShelfHeight;
@@ -125,7 +129,8 @@
 
     public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
             IOneHanded oneHanded, IShellTransitions shellTransitions,
-            IStartingWindow startingWindow) {
+            IStartingWindow startingWindow,
+            ISmartspaceTransitionController smartSpaceTransitionController) {
         unlinkToDeath();
         mSystemUiProxy = proxy;
         mPip = pip;
@@ -133,6 +138,7 @@
         mOneHanded = oneHanded;
         mShellTransitions = shellTransitions;
         mStartingWindow = startingWindow;
+        mSmartspaceTransitionController = smartSpaceTransitionController;
         linkToDeath();
         // re-attach the listeners once missing due to setProxy has not been initialized yet.
         if (mPendingPipAnimationListener != null && mPip != null) {
@@ -147,10 +153,14 @@
             setStartingWindowListener(mPendingStartingWindowListener);
             mPendingStartingWindowListener = null;
         }
+        if (mPendingSmartspaceCallback != null && mSmartspaceTransitionController != null) {
+            setSmartspaceCallback(mPendingSmartspaceCallback);
+            mPendingSmartspaceCallback = null;
+        }
     }
 
     public void clearProxy() {
-        setProxy(null, null, null, null, null, null);
+        setProxy(null, null, null, null, null, null, null);
     }
 
     // TODO(141886704): Find a way to remove this
@@ -642,4 +652,21 @@
             mPendingStartingWindowListener = listener;
         }
     }
+
+
+    //
+    // SmartSpace transitions
+    //
+
+    public void setSmartspaceCallback(ISmartspaceCallback callback) {
+        if (mSmartspaceTransitionController != null) {
+            try {
+                mSmartspaceTransitionController.setSmartspace(callback);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setStartingWindowListener", e);
+            }
+        } else {
+            mPendingSmartspaceCallback = callback;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7956fcc..e52405b 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -30,6 +30,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
@@ -107,6 +108,7 @@
 import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.InputMonitorCompat;
+import com.android.systemui.shared.system.smartspace.ISmartspaceTransitionController;
 import com.android.systemui.shared.tracing.ProtoTraceable;
 import com.android.wm.shell.onehanded.IOneHanded;
 import com.android.wm.shell.pip.IPip;
@@ -164,9 +166,13 @@
                     bundle.getBinder(KEY_EXTRA_SHELL_SHELL_TRANSITIONS));
             IStartingWindow startingWindow = IStartingWindow.Stub.asInterface(
                     bundle.getBinder(KEY_EXTRA_SHELL_STARTING_WINDOW));
+            ISmartspaceTransitionController smartspaceTransitionController =
+                    ISmartspaceTransitionController.Stub.asInterface(
+                            bundle.getBinder(KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER));
             MAIN_EXECUTOR.execute(() -> {
                 SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
-                        splitscreen, onehanded, shellTransitions, startingWindow);
+                        splitscreen, onehanded, shellTransitions, startingWindow,
+                        smartspaceTransitionController);
                 TouchInteractionService.this.initInputMonitor();
                 preloadOverview(true /* fromInit */);
                 mDeviceState.runOnUserUnlocked(() -> {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index 8d9c524..bc20902 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -147,12 +147,10 @@
     }
 
     private void onStartGestureDetected() {
-        if (mDeviceState.isOneHandedModeEnabled()) {
-            if (!mDeviceState.isOneHandedModeActive()) {
-                SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode();
-            }
-        } else if (mDeviceState.isSwipeToNotificationEnabled()) {
+        if (mDeviceState.isSwipeToNotificationEnabled()) {
             SystemUiProxy.INSTANCE.get(mContext).expandNotificationPanel();
+        } else if (!mDeviceState.isOneHandedModeActive()) {
+            SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode();
         }
     }
 
diff --git a/res/drawable/all_apps_tabs_background.xml b/res/drawable/all_apps_tabs_background.xml
index f882522..a345dd3 100644
--- a/res/drawable/all_apps_tabs_background.xml
+++ b/res/drawable/all_apps_tabs_background.xml
@@ -16,8 +16,9 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
     <item
-        android:top="6dp"
-        android:bottom="6dp">
+        android:top="@dimen/all_apps_tabs_vertical_padding"
+        android:bottom="@dimen/all_apps_tabs_vertical_padding
+">
         <shape android:shape="rectangle">
             <solid android:color="?androidprv:attr/colorSurface" />
             <corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
diff --git a/res/layout/arrow_toast.xml b/res/layout/arrow_toast.xml
index ae60e1b..c071bec 100644
--- a/res/layout/arrow_toast.xml
+++ b/res/layout/arrow_toast.xml
@@ -14,9 +14,7 @@
      limitations under the License.
 -->
 
-<merge
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content">
 
@@ -40,7 +38,8 @@
             android:gravity="center"
             android:layout_gravity="center_vertical"
             android:textColor="@color/arrow_tip_view_content"
-            android:textSize="16sp"/>
+            android:textSize="16sp" />
+
         <ImageView
             android:id="@+id/dismiss"
             android:layout_width="40dp"
@@ -53,13 +52,13 @@
             android:src="@drawable/ic_remove_no_shadow"
             android:tint="@color/arrow_tip_view_content"
             android:background="?android:attr/selectableItemBackgroundBorderless"
-            android:contentDescription="@string/accessibility_close"/>
+            android:contentDescription="@string/accessibility_close" />
     </LinearLayout>
 
     <View
         android:id="@+id/arrow"
         android:elevation="2dp"
-        android:layout_width="10dp"
+        android:layout_width="@dimen/arrow_toast_arrow_width"
         android:layout_height="8dp"
         android:layout_marginTop="-2dp"/>
 </merge>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 394aecb..c5f36ce 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -105,6 +105,7 @@
     <dimen name="all_apps_tip_bottom_margin">8dp</dimen>
     <!-- The size of corner radius of the arrow in the arrow toast. -->
     <dimen name="arrow_toast_corner_radius">2dp</dimen>
+    <dimen name="arrow_toast_arrow_width">10dp</dimen>
 
 <!-- Search bar in All Apps -->
     <dimen name="all_apps_header_max_elevation">3dp</dimen>
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index d977011..d18138f 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -20,8 +20,10 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.graphics.Point;
+import android.graphics.Rect;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
@@ -110,6 +112,47 @@
         assertThat(info.minSpanY).isEqualTo(2);
     }
 
+    @Test
+    public void initSpans_minResizeWidthWithCellSpacingAndWidgetInset_shouldInitializeMinSpans() {
+        InvariantDeviceProfile idp = createIDP();
+        DeviceProfile dp = idp.supportedProfiles.get(0);
+        Rect padding = new Rect();
+        AppWidgetHostView.getDefaultPaddingForWidget(mContext, null, padding);
+        int maxPadding = Math.max(Math.max(padding.left, padding.right),
+                Math.max(padding.top, padding.bottom));
+        dp.cellLayoutBorderSpacingPx = maxPadding + 1;
+        Mockito.when(dp.shouldInsetWidgets()).thenReturn(true);
+
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minResizeWidth = CELL_SIZE * 2 + maxPadding;
+        info.minResizeHeight = CELL_SIZE * 2 + maxPadding;
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(2);
+        assertThat(info.minSpanY).isEqualTo(2);
+    }
+
+    @Test
+    public void initSpans_minResizeWidthWithCellSpacingAndNoWidgetInset_shouldInitializeMinSpans() {
+        InvariantDeviceProfile idp = createIDP();
+        DeviceProfile dp = idp.supportedProfiles.get(0);
+        Rect padding = new Rect();
+        AppWidgetHostView.getDefaultPaddingForWidget(mContext, null, padding);
+        int maxPadding = Math.max(Math.max(padding.left, padding.right),
+                Math.max(padding.top, padding.bottom));
+        dp.cellLayoutBorderSpacingPx = maxPadding - 1;
+        Mockito.when(dp.shouldInsetWidgets()).thenReturn(false);
+        LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+        info.minResizeWidth = CELL_SIZE * 2 + maxPadding;
+        info.minResizeHeight = CELL_SIZE * 2 + maxPadding;
+
+        info.initSpans(mContext, idp);
+
+        assertThat(info.minSpanX).isEqualTo(3);
+        assertThat(info.minSpanY).isEqualTo(3);
+    }
+
     private InvariantDeviceProfile createIDP() {
         DeviceProfile profile = Mockito.mock(DeviceProfile.class);
         doAnswer(i -> {
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index 4ee365e..ef3df5f 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -53,11 +53,19 @@
 
     protected final BaseDraggingActivity mActivity;
     private final Handler mHandler = new Handler();
+    private final boolean mIsPointingUp;
+    private final int mArrowWidth;
     private Runnable mOnClosed;
 
     public ArrowTipView(Context context) {
+        this(context, false);
+    }
+
+    public ArrowTipView(Context context, boolean isPointingUp) {
         super(context, null, 0);
         mActivity = BaseDraggingActivity.fromContext(context);
+        mIsPointingUp = isPointingUp;
+        mArrowWidth = context.getResources().getDimensionPixelSize(R.dimen.arrow_toast_arrow_width);
         init(context);
     }
 
@@ -105,13 +113,17 @@
         View arrowView = findViewById(R.id.arrow);
         ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
         ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
-                arrowLp.width, arrowLp.height, false));
+                arrowLp.width, arrowLp.height, mIsPointingUp));
         Paint arrowPaint = arrowDrawable.getPaint();
-        arrowPaint.setColor(ContextCompat.getColor(getContext(),  R.color.arrow_tip_view_bg));
+        arrowPaint.setColor(ContextCompat.getColor(getContext(), R.color.arrow_tip_view_bg));
         // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
         arrowPaint.setPathEffect(new CornerPathEffect(
                 context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
         arrowView.setBackground(arrowDrawable);
+        if (mIsPointingUp) {
+            removeView(arrowView);
+            addView(arrowView, 0);
+        }
 
         mIsOpen = true;
 
@@ -128,10 +140,10 @@
     /**
      * Show the ArrowTipView (tooltip) center, start, or end aligned.
      *
-     * @param text The text to be shown in the tooltip.
-     * @param gravity The gravity aligns the tooltip center, start, or end.
+     * @param text             The text to be shown in the tooltip.
+     * @param gravity          The gravity aligns the tooltip center, start, or end.
      * @param arrowMarginStart The margin from start to place arrow (ignored if center)
-     * @param top  The Y coordinate of the bottom of tooltip.
+     * @param top              The Y coordinate of the bottom of tooltip.
      * @return The tooltip.
      */
     public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
@@ -149,15 +161,15 @@
             arrowMarginStart = parent.getMeasuredWidth() - arrowMarginStart;
         }
         if (gravity == Gravity.END) {
-            lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart);
+            lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart - mArrowWidth);
         } else if (gravity == Gravity.START) {
-            lp.setMarginStart(arrowMarginStart);
+            lp.setMarginStart(arrowMarginStart - mArrowWidth / 2);
         }
         requestLayout();
 
         params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
         params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
-        post(() -> setY(top - getHeight()));
+        post(() -> setY(top - (mIsPointingUp ? 0 : getHeight())));
         setAlpha(0);
         animate()
                 .alpha(1f)
@@ -172,13 +184,14 @@
     /**
      * Show the ArrowTipView (tooltip) custom aligned.
      *
-     * @param text The text to be shown in the tooltip.
+     * @param text        The text to be shown in the tooltip.
      * @param arrowXCoord The X coordinate for the arrow on the tip. The arrow is usually in the
      *                    center of ArrowTipView unless the ArrowTipView goes beyond screen margin.
-     * @param yCoord The Y coordinate of the bottom of the tooltip.
+     * @param yCoord      The Y coordinate of the bottom of the tooltip.
      * @return The tool tip view.
      */
-    @Nullable public ArrowTipView showAtLocation(String text, int arrowXCoord, int yCoord) {
+    @Nullable
+    public ArrowTipView showAtLocation(String text, int arrowXCoord, int yCoord) {
         ViewGroup parent = mActivity.getDragLayer();
         @Px int parentViewWidth = parent.getWidth();
         @Px int textViewWidth = getContext().getResources()
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index 53b5fec..14108f6 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -67,61 +67,86 @@
     }
 
     public void initSpans(Context context, InvariantDeviceProfile idp) {
-        // Always assume we're working with the smallest span to make sure we
-        // reserve enough space in both orientations.
-        float smallestCellWidth = Float.MAX_VALUE;
-        float smallestCellHeight = Float.MAX_VALUE;
+        int minSpanX = 0;
+        int minSpanY = 0;
+        int maxSpanX = idp.numColumns;
+        int maxSpanY = idp.numRows;
+        int spanX = 0;
+        int spanY = 0;
+
+        Rect widgetPadding = new Rect();
+        Rect localPadding = new Rect();
+        AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
 
         Point cellSize = new Point();
-        boolean isWidgetPadded = false;
         for (DeviceProfile dp : idp.supportedProfiles) {
             dp.getCellSize(cellSize);
-            smallestCellWidth = Math.min(smallestCellWidth, cellSize.x);
-            smallestCellHeight = Math.min(smallestCellHeight, cellSize.y);
-            isWidgetPadded = isWidgetPadded || !dp.shouldInsetWidgets();
+            // We want to account for the extra amount of padding that we are adding to the widget
+            // to ensure that it gets the full amount of space that it has requested.
+            // If grids supports insetting widgets, we do not account for widget padding.
+            if (dp.shouldInsetWidgets()) {
+                localPadding.setEmpty();
+            } else {
+                localPadding.set(widgetPadding);
+            }
+            minSpanX = Math.max(minSpanX,
+                    getSpanX(localPadding, minResizeWidth, dp.cellLayoutBorderSpacingPx,
+                            cellSize.x));
+            minSpanY = Math.max(minSpanY,
+                    getSpanY(localPadding, minResizeHeight, dp.cellLayoutBorderSpacingPx,
+                            cellSize.y));
+
+            if (ATLEAST_S) {
+                if (maxResizeWidth > 0) {
+                    maxSpanX = Math.min(maxSpanX,
+                            getSpanX(localPadding, maxResizeWidth, dp.cellLayoutBorderSpacingPx,
+                                    cellSize.x));
+                }
+                if (maxResizeHeight > 0) {
+                    maxSpanY = Math.min(maxSpanY,
+                            getSpanY(localPadding, maxResizeHeight, dp.cellLayoutBorderSpacingPx,
+                                    cellSize.y));
+                }
+            }
+
+            spanX = Math.max(spanX,
+                    getSpanX(localPadding, minWidth, dp.cellLayoutBorderSpacingPx, cellSize.x));
+            spanY = Math.max(spanY,
+                    getSpanY(localPadding, minHeight, dp.cellLayoutBorderSpacingPx, cellSize.y));
         }
 
-        // We want to account for the extra amount of padding that we are adding to the widget
-        // to ensure that it gets the full amount of space that it has requested.
-        // If grids supports insetting widgets, we do not account for widget padding.
-        Rect widgetPadding = new Rect();
-        if (isWidgetPadded) {
-            AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
+        if (ATLEAST_S) {
+            // Ensures maxSpan >= minSpan
+            maxSpanX = Math.max(maxSpanX, minSpanX);
+            maxSpanY = Math.max(maxSpanY, minSpanY);
+
+            // Use targetCellWidth/Height if it is within the min/max ranges.
+            // Otherwise, use the span of minWidth/Height.
+            if (targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
+                    && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
+                spanX = targetCellWidth;
+                spanY = targetCellHeight;
+            }
         }
 
-        minSpanX = getSpanX(widgetPadding, minResizeWidth, smallestCellWidth);
-        minSpanY = getSpanY(widgetPadding, minResizeHeight, smallestCellHeight);
-
-        // Use maxResizeWidth/Height if they are defined and we're on S or above.
-        maxSpanX =
-                (ATLEAST_S && maxResizeWidth > 0)
-                        ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)
-                        : idp.numColumns;
-        maxSpanY =
-                (ATLEAST_S && maxResizeHeight > 0)
-                        ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)
-                        : idp.numRows;
-
-        // Use targetCellWidth/Height if it is within the min/max ranges and we're on S or above.
-        // Otherwise, fall back to minWidth/Height.
-        if (ATLEAST_S && targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
-                && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
-            spanX = targetCellWidth;
-            spanY = targetCellHeight;
-        } else {
-            spanX = getSpanX(widgetPadding, minWidth, smallestCellWidth);
-            spanY = getSpanY(widgetPadding, minHeight, smallestCellHeight);
-        }
+        this.minSpanX = minSpanX;
+        this.minSpanY = minSpanY;
+        this.maxSpanX = maxSpanX;
+        this.maxSpanY = maxSpanY;
+        this.spanX = spanX;
+        this.spanY = spanY;
     }
 
-    private int getSpanX(Rect widgetPadding, int widgetWidth, float cellWidth) {
+    private int getSpanX(Rect widgetPadding, int widgetWidth, int cellSpacing, float cellWidth) {
         return Math.max(1, (int) Math.ceil(
-                (widgetWidth + widgetPadding.left + widgetPadding.right) / cellWidth));
+                (widgetWidth + widgetPadding.left + widgetPadding.right + cellSpacing) / (cellWidth
+                        + cellSpacing)));
     }
 
-    private int getSpanY(Rect widgetPadding, int widgetHeight, float cellHeight) {
+    private int getSpanY(Rect widgetPadding, int widgetHeight, int cellSpacing, float cellHeight) {
         return Math.max(1, (int) Math.ceil(
-                (widgetHeight + widgetPadding.top + widgetPadding.bottom) / cellHeight));
+                (widgetHeight + widgetPadding.top + widgetPadding.bottom + cellSpacing) / (
+                        cellHeight + cellSpacing)));
     }
 
     public String getLabel(PackageManager packageManager) {