Merge "Move numAllAppsColumns to displayOptions" into sc-dev
diff --git a/go/quickstep/res/values/strings.xml b/go/quickstep/res/values/strings.xml
index 71e2f3a..61c8cd9 100644
--- a/go/quickstep/res/values/strings.xml
+++ b/go/quickstep/res/values/strings.xml
@@ -10,5 +10,5 @@
     <!-- Label for a button that translates a screenshot of the current app. [CHAR_LIMIT=40] -->
     <string name="action_translate">Translate</string>
     <!-- Label for a button that triggers Search on a screenshot of the current app. [CHAR_LIMIT=40] -->
-    <string name="action_search">Search</string>
+    <string name="action_search">Lens</string>
 </resources>
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 36a4e7f..872f168 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -36,6 +36,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.launcher3.R;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.TaskThumbnailView;
@@ -51,6 +53,7 @@
     public static final String ACTION_SEARCH = "com.android.quickstep.ACTION_SEARCH";
     public static final String ELAPSED_NANOS = "niu_actions_elapsed_realtime_nanos";
     public static final String ACTIONS_URL = "niu_actions_app_url";
+    private static final String ASSIST_KEY_CONTENT = "content";
     private static final String TAG = "TaskOverlayFactoryGo";
 
     // Empty constructor required for ResourceBasedOverride
@@ -68,8 +71,6 @@
      * @param <T> The type of View in which the overlay will be placed
      */
     public static final class TaskOverlayGo<T extends OverviewActionsView> extends TaskOverlay {
-        private static final String ASSIST_KEY_CONTENT = "content";
-
         private String mNIUPackageName;
         private int mTaskId;
         private Bundle mAssistData;
@@ -95,7 +96,7 @@
             }
 
             getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
-            boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+            boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
             getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
 
             mTaskId = task.key.id;
@@ -122,7 +123,11 @@
             });
         }
 
-        private void sendNIUIntent(String actionType) {
+        /**
+         * Creates and sends an Intent corresponding to the button that was clicked
+         */
+        @VisibleForTesting
+        public void sendNIUIntent(String actionType) {
             Intent intent = createNIUIntent(actionType);
             mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
         }
@@ -178,6 +183,11 @@
                 }
             }
         }
+
+        @VisibleForTesting
+        public void setImageActionsAPI(ImageActionsApi imageActionsApi) {
+            mImageApi = imageActionsApi;
+        }
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index cd13200..1ad5f2c 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -176,7 +176,7 @@
 
             if (thumbnail != null) {
                 getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
-                boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+                boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
                 getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
             }
         }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 335d470..d357ebe 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3152,7 +3152,9 @@
             MAIN_EXECUTOR.execute(() -> {
                 // Needed for activities that auto-enter PiP, which will not trigger a remote
                 // animation to be created
-                mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+                if (mActivity != null) {
+                    mActivity.clearForceInvisibleFlag(STATE_HANDLER_INVISIBILITY_FLAGS);
+                }
             });
         }
     }
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 2b54f95..af62582 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -440,13 +440,14 @@
     }
 
     /**
-     * Returns whether the snapshot is real.
+     * Returns whether the snapshot is real. If the device is locked for the user of the task,
+     * the snapshot used will be an app-theme generated snapshot instead of a real snapshot.
      */
     public boolean isRealSnapshot() {
         if (mThumbnailData == null) {
             return false;
         }
-        return mThumbnailData.isRealSnapshot;
+        return mThumbnailData.isRealSnapshot && !mTask.isLocked;
     }
 
     /**
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 1da6bc4..c1c8f01 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -132,6 +132,7 @@
         <attr name="defaultLayoutId" format="reference" />
         <attr name="demoModeLayoutId" format="reference" />
         <attr name="isScalable" format="boolean" />
+        <attr name="devicePaddingId" format="reference" />
 
     </declare-styleable>
 
diff --git a/res/xml/size_limits.xml b/res/xml/size_limits_80x104.xml
similarity index 98%
rename from res/xml/size_limits.xml
rename to res/xml/size_limits_80x104.xml
index ba57014..e11bc5e 100644
--- a/res/xml/size_limits.xml
+++ b/res/xml/size_limits_80x104.xml
@@ -38,7 +38,7 @@
         <workspaceBottomPadding
             launcher:a="0.50"
             launcher:b="0"
-            launcher:c="-16dp"/>
+            launcher:c="16dp"/>
         <hotseatBottomPadding
             launcher:a="0.50"
             launcher:b="0"
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
index 4827f36..7c387b1 100644
--- a/src/com/android/launcher3/DevicePaddings.java
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -52,8 +52,8 @@
 
     ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
 
-    public DevicePaddings(Context context) {
-        try (XmlResourceParser parser = context.getResources().getXml(R.xml.size_limits)) {
+    public DevicePaddings(Context context, int devicePaddingId) {
+        try (XmlResourceParser parser = context.getResources().getXml(devicePaddingId)) {
             final int depth = parser.getDepth();
             int type;
             while (((type = parser.next()) != XmlPullParser.END_TAG ||
@@ -94,16 +94,27 @@
                             if (workspaceTopPadding == null
                                     || workspaceBottomPadding == null
                                     || hotseatBottomPadding == null) {
-                                throw new RuntimeException("DevicePadding missing padding.");
+                                if (Utilities.IS_DEBUG_DEVICE) {
+                                    throw new RuntimeException("DevicePadding missing padding.");
+                                }
                             }
 
-                            mDevicePaddings.add(new DevicePadding(maxWidthPx, workspaceTopPadding,
-                                    workspaceBottomPadding, hotseatBottomPadding));
+                            DevicePadding dp = new DevicePadding(maxWidthPx, workspaceTopPadding,
+                                    workspaceBottomPadding, hotseatBottomPadding);
+                            if (dp.isValid()) {
+                                mDevicePaddings.add(dp);
+                            } else {
+                                Log.e(TAG, "Invalid device padding found.");
+                                if (Utilities.IS_DEBUG_DEVICE) {
+                                    throw new RuntimeException("DevicePadding is invalid");
+                                }
+                            }
                         }
                     }
                 }
             }
         } catch (IOException | XmlPullParserException e) {
+            Log.e(TAG, "Failure parsing device padding layout.", e);
             throw new RuntimeException(e);
         }
 
@@ -128,6 +139,9 @@
      */
     public static final class DevicePadding {
 
+        // One for each padding since they can each be off by 1 due to rounding errors.
+        private static final int ROUNDING_THRESHOLD_PX = 3;
+
         private final int maxEmptySpacePx;
         private final PaddingFormula workspaceTopPadding;
         private final PaddingFormula workspaceBottomPadding;
@@ -143,6 +157,10 @@
             this.hotseatBottomPadding = hotseatBottomPadding;
         }
 
+        public int getMaxEmptySpacePx() {
+            return maxEmptySpacePx;
+        }
+
         public int getWorkspaceTopPadding(int extraSpacePx) {
             return workspaceTopPadding.calculate(extraSpacePx);
         }
@@ -154,6 +172,22 @@
         public int getHotseatBottomPadding(int extraSpacePx) {
             return hotseatBottomPadding.calculate(extraSpacePx);
         }
+
+        public boolean isValid() {
+            int workspaceTopPadding = getWorkspaceTopPadding(maxEmptySpacePx);
+            int workspaceBottomPadding = getWorkspaceBottomPadding(maxEmptySpacePx);
+            int hotseatBottomPadding = getHotseatBottomPadding(maxEmptySpacePx);
+            int sum = workspaceTopPadding + workspaceBottomPadding + hotseatBottomPadding;
+            int diff = Math.abs(sum - maxEmptySpacePx);
+            if (DEBUG) {
+                Log.d(TAG, "isValid: workspaceTopPadding=" + workspaceTopPadding
+                        + ", workspaceBottomPadding=" + workspaceBottomPadding
+                        + ", hotseatBottomPadding=" + hotseatBottomPadding
+                        + ", sum=" + sum
+                        + ", diff=" + diff);
+            }
+            return diff <= ROUNDING_THRESHOLD_PX;
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 58d612d..1ce5f4d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -104,6 +104,7 @@
     private final int mWorkspacePageIndicatorOverlapWorkspace;
 
     // Workspace icons
+    public float iconScale;
     public int iconSizePx;
     public int iconTextSizePx;
     public int iconDrawablePaddingPx;
@@ -312,12 +313,19 @@
         // Calculate all of the remaining variables.
         extraSpace = updateAvailableDimensions(res);
         // Now that we have all of the variables calculated, we can tune certain sizes.
-        if (isScalableGrid) {
-            DevicePadding padding = inv.devicePaddings.getDevicePadding(extraSpace);
-            workspaceTopPadding = padding.getWorkspaceTopPadding(extraSpace);
-            workspaceBottomPadding = padding.getWorkspaceBottomPadding(extraSpace);
+        if (isScalableGrid && inv.devicePaddings != null) {
+            // Paddings were created assuming no scaling, so we first unscale the extra space.
+            int unscaledExtraSpace = (int) (extraSpace / iconScale);
+            DevicePadding padding = inv.devicePaddings.getDevicePadding(unscaledExtraSpace);
 
-            extraHotseatBottomPadding = padding.getHotseatBottomPadding(extraSpace);
+            int paddingWorkspaceTop = padding.getWorkspaceTopPadding(unscaledExtraSpace);
+            int paddingWorkspaceBottom = padding.getWorkspaceBottomPadding(unscaledExtraSpace);
+            int paddingHotseatBottom = padding.getHotseatBottomPadding(unscaledExtraSpace);
+
+            workspaceTopPadding = Math.round(paddingWorkspaceTop * iconScale);
+            workspaceBottomPadding = Math.round(paddingWorkspaceBottom * iconScale);
+            extraHotseatBottomPadding = Math.round(paddingHotseatBottom * iconScale);
+
             hotseatBarSizePx += extraHotseatBottomPadding;
             hotseatBarBottomPaddingPx += extraHotseatBottomPadding;
         } else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
@@ -490,6 +498,8 @@
      * hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
      */
     public void updateIconSize(float scale, Resources res) {
+        iconScale = scale;
+
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
         float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
@@ -890,7 +900,14 @@
         writer.println(prefix + pxToDpStr("workspacePadding.right", workspacePadding.right));
         writer.println(prefix + pxToDpStr("workspacePadding.bottom", workspacePadding.bottom));
 
+        writer.println(prefix + pxToDpStr("scaleToFit", iconScale));
         writer.println(prefix + pxToDpStr("extraSpace", extraSpace));
+
+        if (inv.devicePaddings != null) {
+            int unscaledExtraSpace = (int) (extraSpace / iconScale);
+            writer.println(prefix + pxToDpStr("maxEmptySpace",
+                    inv.devicePaddings.getDevicePadding(unscaledExtraSpace).getMaxEmptySpacePx()));
+        }
         writer.println(prefix + pxToDpStr("workspaceTopPadding", workspaceTopPadding));
         writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
         writer.println(prefix + pxToDpStr("extraHotseatBottomPadding", extraHotseatBottomPadding));
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 320b97c..b0c3bb4 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -132,6 +132,7 @@
      * Do not query directly. see {@link DeviceProfile#isScalableGrid}.
      */
     protected boolean isScalable;
+    public int devicePaddingId;
 
     public String dbFile;
     public int defaultLayoutId;
@@ -140,7 +141,7 @@
     public DeviceProfile landscapeProfile;
     public DeviceProfile portraitProfile;
 
-    public DevicePaddings devicePaddings;
+    @Nullable public DevicePaddings devicePaddings;
 
     public Point defaultWallpaperSize;
     public Rect defaultWidgetPadding;
@@ -165,6 +166,7 @@
         numHotseatIcons = p.numHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
         isScalable = p.isScalable;
+        devicePaddingId = p.devicePaddingId;
         minCellHeight = p.minCellHeight;
         minCellWidth = p.minCellWidth;
         borderSpacing = p.borderSpacing;
@@ -236,7 +238,6 @@
         result.minCellWidth = defaultDisplayOption.minCellWidth;
         result.borderSpacing = defaultDisplayOption.borderSpacing;
 
-        devicePaddings = new DevicePaddings(context);
         initGrid(context, myInfo, result);
     }
 
@@ -267,7 +268,6 @@
         ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
 
         DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
-        devicePaddings = new DevicePaddings(context);
         initGrid(context, displayInfo, displayOption);
         return displayOption.grid.name;
     }
@@ -284,6 +284,7 @@
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
         isScalable = closestProfile.isScalable;
+        devicePaddingId = closestProfile.devicePaddingId;
 
         mExtraAttrs = closestProfile.extraAttrs;
 
@@ -307,6 +308,10 @@
             allAppsIconTextSize = iconTextSize;
         }
 
+        if (devicePaddingId != 0) {
+            devicePaddings = new DevicePaddings(context, devicePaddingId);
+        }
+
         // If the partner customization apk contains any grid overrides, apply them
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
@@ -619,6 +624,7 @@
         private final int demoModeLayoutId;
 
         private final boolean isScalable;
+        private final int devicePaddingId;
 
         private final SparseArray<TypedValue> extraAttrs;
 
@@ -643,6 +649,8 @@
 
             isScalable = a.getBoolean(
                     R.styleable.GridDisplayOption_isScalable, false);
+            devicePaddingId = a.getResourceId(
+                    R.styleable.GridDisplayOption_devicePaddingId, 0);
 
             a.recycle();
 
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index 0ea0290..5be9529 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -42,43 +42,16 @@
                 }
             };
 
-    /**
-     * Determines how each alpha should factor into the final alpha.
-     */
-    public enum Mode {
-        BLEND() {
-            @Override
-            public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
-                return currentAlpha * otherAlpha;
-            }
-        },
-
-        MAX() {
-            @Override
-            public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
-                return Math.max(currentAlpha, otherAlpha);
-            }
-        };
-
-        protected abstract float calculateNewAlpha(float currentAlpha, float otherAlpha);
-    }
-
     private final View mView;
     private final AlphaProperty[] mMyProperties;
-    private final Mode mMode;
 
     private int mValidMask;
     // Whether we should change from INVISIBLE to VISIBLE and vice versa at low alpha values.
     private boolean mUpdateVisibility;
 
     public MultiValueAlpha(View view, int size) {
-        this(view, size, Mode.BLEND);
-    }
-
-    public MultiValueAlpha(View view, int size, Mode mode) {
         mView = view;
         mMyProperties = new AlphaProperty[size];
-        mMode = mode;
 
         mValidMask = 0;
         for (int i = 0; i < size; i++) {
@@ -124,7 +97,7 @@
                 mOthers = 1;
                 for (AlphaProperty prop : mMyProperties) {
                     if (prop != this) {
-                        mOthers = mMode.calculateNewAlpha(mOthers, prop.mValue);
+                        mOthers *= prop.mValue;
                     }
                 }
             }
@@ -134,7 +107,7 @@
             mValidMask = mMyMask;
             mValue = value;
 
-            mView.setAlpha(mMode.calculateNewAlpha(mOthers, mValue));
+            mView.setAlpha(mOthers * mValue);
             if (mUpdateVisibility) {
                 AlphaUpdateListener.updateVisibility(mView);
             }
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 2fda86c..bbb0d92 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -26,6 +26,7 @@
 import android.util.Pair;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.Interpolator;
@@ -152,6 +153,19 @@
         });
     }
 
+    @Override
+    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mNoIntercept = false;
+            ScrollView scrollView = findViewById(R.id.widgets_table_scroll_view);
+            if (getPopupContainer().isEventOverView(scrollView, ev)
+                    && scrollView.getScrollY() > 0) {
+                mNoIntercept = true;
+            }
+        }
+        return super.onControllerInterceptTouchEvent(ev);
+    }
+
     protected WidgetCell addItemCell(ViewGroup parent) {
         WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
                 .inflate(R.layout.widget_cell, parent, false);