Launcher changes for go/widget-size-specification

Makes sure the launcher:

1 - Send the list of actual sizes the widget in all situations.
2 - Gives the current size to the framework on inflation.

Also needed to guard the new border changes introduced in
http://ag/13532637 with the corresponding flag.

Change-Id: I2a33e9501b921f2fc393684e8ce91ee077626bf7
Test: By hand using a local widget.
Bug: 179025145
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 1330ed4..be92c5d 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -2,6 +2,8 @@
 
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
+import static com.android.launcher3.Utilities.ATLEAST_S;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
 import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
 
@@ -11,14 +13,19 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.FocusLogic;
@@ -352,33 +359,99 @@
     }
 
     public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
-            int spanX, int spanY) {
-        getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
-        widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
-                sTmpRect.right, sTmpRect.bottom);
+                                              int spanX, int spanY) {
+        List<PointF> sizes = getWidgetSizes(launcher, spanX, spanY);
+        if (ATLEAST_S) {
+            widgetView.updateAppWidgetSize(new Bundle(), sizes);
+        } else {
+            Rect bounds = getMinMaxSizes(sizes, null /* outRect */);
+            widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
+                    bounds.bottom);
+        }
     }
 
-    public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
-        if (rect == null) {
-            rect = new Rect();
-        }
+    private static PointF getWidgetSize(Context context, Point cellSize, int spanX, int spanY) {
         final float density = context.getResources().getDisplayMetrics().density;
+        float hBorderSpacing = 0;
+        float vBorderSpacing = 0;
+        if (ENABLE_FOUR_COLUMNS.get()) {
+            final int borderSpacing = context.getResources()
+                    .getDimensionPixelSize(R.dimen.dynamic_grid_cell_border_spacing);
+            hBorderSpacing = (spanX - 1) * borderSpacing;
+            vBorderSpacing = (spanY - 1) * borderSpacing;
+        }
+        PointF widgetSize = new PointF();
+        widgetSize.x = ((spanX * cellSize.x) + hBorderSpacing) / density;
+        widgetSize.y = ((spanY * cellSize.y) + vBorderSpacing) / density;
+        return widgetSize;
+    }
+
+    /** Returns the actual widget size given its span. */
+    public static PointF getWidgetSize(Context context, int spanX, int spanY) {
+        final Point[] cellSize = CELL_SIZE.get(context);
+        if (isLandscape(context)) {
+            return getWidgetSize(context, cellSize[0], spanX, spanY);
+        }
+        return getWidgetSize(context, cellSize[1], spanX, spanY);
+    }
+
+    /** Returns true if the screen is in landscape mode. */
+    private static boolean isLandscape(Context context) {
+        return context.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE;
+    }
+
+    /** Returns the list of sizes for a widget of given span, in dp. */
+    public static ArrayList<PointF> getWidgetSizes(Context context, int spanX, int spanY) {
         final Point[] cellSize = CELL_SIZE.get(context);
 
-        final int borderSpacing = context.getResources()
-                .getDimensionPixelSize(R.dimen.dynamic_grid_cell_border_spacing);
-        final float hBorderSpacing = (spanX - 1) * borderSpacing;
-        final float vBorderSpacing = (spanY - 1) * borderSpacing;
+        PointF landSize = getWidgetSize(context, cellSize[0], spanX, spanY);
+        PointF portSize = getWidgetSize(context, cellSize[1], spanX, spanY);
 
-        // Compute landscape size
-        int landWidth = (int) (((spanX * cellSize[0].x) + hBorderSpacing) / density);
-        int landHeight = (int) (((spanY * cellSize[0].y) + vBorderSpacing) / density);
+        ArrayList<PointF> sizes = new ArrayList<>(2);
+        sizes.add(landSize);
+        sizes.add(portSize);
+        return sizes;
+    }
 
-        // Compute portrait size
-        int portWidth = (int) (((spanX * cellSize[1].x) + hBorderSpacing) / density);
-        int portHeight = (int) (((spanY * cellSize[1].y) + vBorderSpacing) / density);
-        rect.set(portWidth, landHeight, landWidth, portHeight);
-        return rect;
+    /**
+     * Returns the min and max widths and heights given a list of sizes, in dp.
+     *
+     * @param sizes List of sizes to get the min/max from.
+     * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If
+     *               null, a new rectangle will be allocated.
+     * @return A rectangle with the left (resp. top) is used for the min width (resp. height) and
+     * the right (resp. bottom) for the max. The returned rectangle is set with 0s if the list is
+     * empty.
+     */
+    public static Rect getMinMaxSizes(List<PointF> sizes, @Nullable Rect outRect) {
+        if (outRect == null) {
+            outRect = new Rect();
+        }
+        if (sizes.isEmpty()) {
+            outRect.set(0, 0, 0, 0);
+        } else {
+            PointF first = sizes.get(0);
+            outRect.set((int) first.x, (int) first.y, (int) first.x, (int) first.y);
+            for (int i = 1; i < sizes.size(); i++) {
+                outRect.union((int) sizes.get(i).x, (int) sizes.get(i).y);
+            }
+        }
+        return outRect;
+    }
+
+    /**
+     * Returns the range of sizes a widget may be displayed, given its span.
+     *
+     * @param context Context in which the View is rendered.
+     * @param spanX Width of the widget, in cells.
+     * @param spanY Height of the widget, in cells.
+     * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If
+     *               null, a new rectangle will be allocated.
+     */
+    public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY,
+            @Nullable Rect outRect) {
+        return getMinMaxSizes(getWidgetSizes(context, spanX, spanY), outRect);
     }
 
     @Override
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index cd4616a..db7fd3f 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -357,10 +357,8 @@
         }
 
         layout.markCellsAsOccupiedForView(host);
-        Rect sizeRange = new Rect();
-        AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange);
-        ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
-                sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
+        AppWidgetResizeFrame.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
+                info.spanX, info.spanY);
         host.requestLayout();
         mLauncher.getModelWriter().updateItemInDatabase(info);
         announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 289e0d8..459aefe 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -20,6 +20,8 @@
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER;
 
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.SearchManager;
@@ -30,6 +32,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -50,6 +53,8 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.FragmentWithPreview;
 
+import java.util.ArrayList;
+
 /**
  * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
  * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
@@ -294,12 +299,16 @@
             InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
 
             Bundle opts = new Bundle();
-            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(getContext(),
-                    idp.numColumns, 1, null);
+            ArrayList<PointF> sizes = AppWidgetResizeFrame
+                    .getWidgetSizes(getContext(), idp.numColumns, 1);
+            Rect size = AppWidgetResizeFrame.getMinMaxSizes(sizes, null /* outRect */);
             opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
             opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
             opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
             opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+            if (ATLEAST_S) {
+                opts.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes);
+            }
             return opts;
         }
 
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 780a1a1..41098f9 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,9 +16,12 @@
 
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.PointF;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.SparseBooleanArray;
@@ -70,8 +73,6 @@
     private boolean mIsAutoAdvanceRegistered;
     private Runnable mAutoAdvanceRunnable;
 
-
-
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mLauncher = Launcher.getLauncher(context);
@@ -219,6 +220,16 @@
     }
 
     @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        if (ATLEAST_S) {
+            float density = getContext().getResources().getDisplayMetrics().density;
+            setCurrentSize(new PointF(w / density, h / density));
+        }
+    }
+
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(getClass().getName());
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index ca47728..8c3206d 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -46,6 +47,8 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.util.Themes;
 
+import java.util.List;
+
 public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
         implements OnClickListener, ItemInfoUpdateReceiver {
     private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
@@ -109,6 +112,11 @@
     }
 
     @Override
+    public void updateAppWidgetSize(Bundle newOptions, List<PointF> sizes) {
+        // No-op
+    }
+
+    @Override
     protected View getDefaultView() {
         View defaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
         defaultView.setOnClickListener(this);
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index c022374..2438bdf 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -3,6 +3,7 @@
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -18,6 +19,8 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.util.Thunk;
 
+import java.util.ArrayList;
+
 public class WidgetHostViewLoader implements DragController.DragListener {
     private static final String TAG = "WidgetHostViewLoader";
     private static final boolean LOGD = false;
@@ -152,24 +155,28 @@
     }
 
     public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
-        Rect rect = new Rect();
-        AppWidgetResizeFrame.getWidgetSizeRanges(context, info.spanX, info.spanY, rect);
+        ArrayList<PointF> sizes = AppWidgetResizeFrame
+                .getWidgetSizes(context, info.spanX, info.spanY);
+
         Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
                 info.componentName, null);
-
         float density = context.getResources().getDisplayMetrics().density;
-        int xPaddingDips = (int) ((padding.left + padding.right) / density);
-        int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
+        float xPaddingDips = (padding.left + padding.right) / density;
+        float yPaddingDips = (padding.top + padding.bottom) / density;
+
+        for (PointF size : sizes) {
+            size.x = Math.max(0.f, size.x - xPaddingDips);
+            size.y = Math.max(0.f, size.y - yPaddingDips);
+        }
+
+        Rect rect = AppWidgetResizeFrame.getMinMaxSizes(sizes, null /* outRect */);
 
         Bundle options = new Bundle();
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
-                rect.left - xPaddingDips);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
-                rect.top - yPaddingDips);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
-                rect.right - xPaddingDips);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
-                rect.bottom - yPaddingDips);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
+        options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes);
         return options;
     }
 }