diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 5d9797ff88..74ac8c24c4 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -4,7 +4,6 @@ import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget; 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.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_COMPLETED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RESIZE_STARTED; import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X; @@ -13,18 +12,12 @@ import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; -import android.appwidget.AppWidgetHostView; -import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; -import android.content.ComponentName; import android.content.Context; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; -import android.os.Bundle; import android.util.AttributeSet; -import android.util.SizeF; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -39,10 +32,10 @@ import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.PendingRequestArgs; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import com.android.launcher3.widget.util.WidgetSizes; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener { private static final int SNAP_DURATION = 150; @@ -415,90 +408,12 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mRunningHInc += hSpanDelta; if (!onDismiss) { - updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY); + WidgetSizes.updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY); } } mWidgetView.requestLayout(); } - public static void updateWidgetSizeRanges( - AppWidgetHostView widgetView, Context context, int spanX, int spanY) { - List sizes = getWidgetSizes(context, spanX, spanY); - if (ATLEAST_S) { - widgetView.updateAppWidgetSize(new Bundle(), sizes); - } else { - Rect bounds = getMinMaxSizes(sizes); - widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right, - bounds.bottom); - } - } - - /** Returns the list of sizes for a widget of given span, in dp. */ - public static ArrayList getWidgetSizes(Context context, int spanX, int spanY) { - ArrayList sizes = new ArrayList<>(2); - final float density = context.getResources().getDisplayMetrics().density; - Point cellSize = new Point(); - - for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) { - final float hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacingPx; - final float vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacingPx; - profile.getCellSize(cellSize); - sizes.add(new SizeF( - ((spanX * cellSize.x) + hBorderSpacing) / density, - ((spanY * cellSize.y) + vBorderSpacing) / density)); - } - return sizes; - } - - /** - * Returns the bundle to be used as the default options for a widget with provided size - */ - public static Bundle getWidgetSizeOptions( - Context context, ComponentName provider, int spanX, int spanY) { - ArrayList sizes = getWidgetSizes(context, spanX, spanY); - Rect padding = getDefaultPaddingForWidget(context, provider, null); - float density = context.getResources().getDisplayMetrics().density; - float xPaddingDips = (padding.left + padding.right) / density; - float yPaddingDips = (padding.top + padding.bottom) / density; - - ArrayList paddedSizes = sizes.stream() - .map(size -> new SizeF( - Math.max(0.f, size.getWidth() - xPaddingDips), - Math.max(0.f, size.getHeight() - yPaddingDips))) - .collect(Collectors.toCollection(ArrayList::new)); - - Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes); - Bundle options = new Bundle(); - 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, paddedSizes); - return options; - } - - /** - * 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. - * @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. - */ - private static Rect getMinMaxSizes(List sizes) { - if (sizes.isEmpty()) { - return new Rect(); - } else { - SizeF first = sizes.get(0); - Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(), - (int) first.getWidth(), (int) first.getHeight()); - for (int i = 1; i < sizes.size(); i++) { - result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight()); - } - return result; - } - } - @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index d136cdadd9..17c8edce6e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -114,6 +114,7 @@ import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; +import com.android.launcher3.widget.util.WidgetSizes; import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; import java.util.ArrayList; @@ -1854,7 +1855,7 @@ public class Workspace extends PagedView item.spanX = resultSpan[0]; item.spanY = resultSpan[1]; AppWidgetHostView awhv = (AppWidgetHostView) cell; - AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0], + WidgetSizes.updateWidgetSizeRanges(awhv, mLauncher, resultSpan[0], resultSpan[1]); } @@ -2528,8 +2529,7 @@ public class Workspace extends PagedView ((PendingAddWidgetInfo) pendingInfo).boundWidget : null; if (finalView != null && updateWidgetSize) { - AppWidgetResizeFrame.updateWidgetSizeRanges(finalView, mLauncher, item.spanX, - item.spanY); + WidgetSizes.updateWidgetSizeRanges(finalView, mLauncher, item.spanX, item.spanY); } int animationStyle = ANIMATE_INTO_POSITION_AND_DISAPPEAR; diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java index 2b36f19226..9faac5b7ef 100644 --- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java +++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java @@ -21,7 +21,6 @@ import android.view.View.AccessibilityDelegate; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.BubbleTextView; import com.android.launcher3.ButtonDropTarget; import com.android.launcher3.CellLayout; @@ -51,6 +50,7 @@ import com.android.launcher3.util.Thunk; import com.android.launcher3.views.OptionsPopupView; import com.android.launcher3.views.OptionsPopupView.OptionItem; import com.android.launcher3.widget.LauncherAppWidgetHostView; +import com.android.launcher3.widget.util.WidgetSizes; import java.util.ArrayList; import java.util.Collections; @@ -367,7 +367,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme } layout.markCellsAsOccupiedForView(host); - AppWidgetResizeFrame.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher, + WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher, info.spanX, info.spanY); host.requestLayout(); mLauncher.getModelWriter().updateItemInDatabase(info); diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java index 003b3bdc22..658c6e1dc6 100644 --- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java @@ -26,13 +26,13 @@ import android.os.Process; import androidx.annotation.Nullable; -import com.android.launcher3.AppWidgetResizeFrame; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; +import com.android.launcher3.widget.util.WidgetSizes; /** * Represents a widget (either instantiated or about to be) in the Launcher. @@ -196,7 +196,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { */ public void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) { if (!mHasNotifiedInitialWidgetSizeChanged) { - AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY); + WidgetSizes.updateWidgetSizeRanges(hostView, launcher, spanX, spanY); mHasNotifiedInitialWidgetSizeChanged = true; } } diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java index 22c3f58574..23ee251772 100644 --- a/src/com/android/launcher3/qsb/QsbContainerView.java +++ b/src/com/android/launcher3/qsb/QsbContainerView.java @@ -20,8 +20,6 @@ import static android.appwidget.AppWidgetManager.ACTION_APPWIDGET_BIND; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER; -import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions; - import android.app.Activity; import android.app.Fragment; import android.app.SearchManager; @@ -49,6 +47,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.FragmentWithPreview; +import com.android.launcher3.widget.util.WidgetSizes; /** * A frame layout which contains a QSB. This internally uses fragment to bind the view, which @@ -292,7 +291,8 @@ public class QsbContainerView extends FrameLayout { protected Bundle createBindOptions() { InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext()); - return getWidgetSizeOptions(getContext(), mWidgetInfo.provider, idp.numColumns, 1); + return WidgetSizes.getWidgetSizeOptions(getContext(), mWidgetInfo.provider, + idp.numColumns, 1); } protected View getDefaultView(ViewGroup container, boolean showSetupIcon) { diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index 3377abbb7b..c04c8dc8b5 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.widget; -import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY; import android.appwidget.AppWidgetHostView; @@ -24,6 +23,7 @@ import android.os.Bundle; import com.android.launcher3.LauncherSettings; import com.android.launcher3.PendingAddItemInfo; +import com.android.launcher3.widget.util.WidgetSizes; /** * Meta data used for late binding of {@link LauncherAppWidgetProviderInfo}. @@ -61,6 +61,6 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { } public Bundle getDefaultSizeOptions(Context context) { - return getWidgetSizeOptions(context, componentName, spanX, spanY); + return WidgetSizes.getWidgetSizeOptions(context, componentName, spanX, spanY); } } diff --git a/src/com/android/launcher3/widget/util/WidgetSizes.java b/src/com/android/launcher3/widget/util/WidgetSizes.java new file mode 100644 index 0000000000..5c8ea7299b --- /dev/null +++ b/src/com/android/launcher3/widget/util/WidgetSizes.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2021 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.widget.util; + +import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget; + +import static com.android.launcher3.Utilities.ATLEAST_S; + +import android.annotation.SuppressLint; +import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.Context; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.Bundle; +import android.util.Size; +import android.util.SizeF; + +import androidx.annotation.Nullable; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherAppState; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** A utility class for widget sizes related calculations. */ +public final class WidgetSizes { + + /** + * Returns the list of all possible sizes, in dp, for a widget of given spans on this device. + */ + public static ArrayList getWidgetSizes(Context context, int spanX, int spanY) { + ArrayList sizes = new ArrayList<>(2); + final float density = context.getResources().getDisplayMetrics().density; + final Point cellSize = new Point(); + + for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) { + Size widgetSizePx = getWidgetSizePx(profile, spanX, spanY, cellSize); + sizes.add(new SizeF(widgetSizePx.getWidth() / density, + widgetSizePx.getHeight() / density)); + } + return sizes; + } + + /** Returns the size, in pixels, a widget of given spans & {@code profile}. */ + public static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY) { + return getWidgetSizePx(profile, spanX, spanY, /* recycledCellSize= */ null); + } + + private static Size getWidgetSizePx(DeviceProfile profile, int spanX, int spanY, + @Nullable Point recycledCellSize) { + final int hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacingPx; + final int vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacingPx; + if (recycledCellSize == null) { + recycledCellSize = new Point(); + } + profile.getCellSize(recycledCellSize); + return new Size(((spanX * recycledCellSize.x) + hBorderSpacing), + ((spanY * recycledCellSize.y) + vBorderSpacing)); + } + + /** + * Updates a given {@code widgetView} with size, {@code spanX}, {@code spanY}. + * + *

On Android S+, it also updates the given {@code widgetView} with a list of sizes derived + * from {@code spanX}, {@code spanY} in all supported device profiles. + */ + @SuppressLint("NewApi") // Already added API check. + public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Context context, + int spanX, int spanY) { + List sizes = getWidgetSizes(context, spanX, spanY); + if (ATLEAST_S) { + widgetView.updateAppWidgetSize(new Bundle(), sizes); + } else { + Rect bounds = getMinMaxSizes(sizes); + widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right, + bounds.bottom); + } + } + + /** + * Returns the bundle to be used as the default options for a widget with provided size. + */ + public static Bundle getWidgetSizeOptions(Context context, ComponentName provider, int spanX, + int spanY) { + ArrayList sizes = getWidgetSizes(context, spanX, spanY); + Rect padding = getDefaultPaddingForWidget(context, provider, null); + float density = context.getResources().getDisplayMetrics().density; + float xPaddingDips = (padding.left + padding.right) / density; + float yPaddingDips = (padding.top + padding.bottom) / density; + + ArrayList paddedSizes = sizes.stream() + .map(size -> new SizeF( + Math.max(0.f, size.getWidth() - xPaddingDips), + Math.max(0.f, size.getHeight() - yPaddingDips))) + .collect(Collectors.toCollection(ArrayList::new)); + + Rect rect = getMinMaxSizes(paddedSizes); + Bundle options = new Bundle(); + 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, paddedSizes); + return options; + } + + /** + * 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. + * @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. + */ + private static Rect getMinMaxSizes(List sizes) { + if (sizes.isEmpty()) { + return new Rect(); + } else { + SizeF first = sizes.get(0); + Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(), + (int) first.getWidth(), (int) first.getHeight()); + for (int i = 1; i < sizes.size(); i++) { + result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight()); + } + return result; + } + } +}