Resize preview for correct clipping
Before, the remote view for the widget in launcher is reused to generate a new preview. However, measuring the view without changing the scale would cause strange clippings. This CL sets the scale of the widget views by manually computing the size ratio.
Change ag/19572297 is necessary before a complete clean-up.
Test: Create a weather widget on first screen -> go to Wallpaper & style -> App grid -> tap on a different grid and verify that the clipping is correct
Fix: 228328759
Change-Id: I8242d3bcfcf30ec924552c1320e22f8a3592f1c1
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index f27eb79..596b15e 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -38,11 +38,13 @@
import android.view.Surface;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.DevicePaddings.DevicePadding;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
@@ -57,6 +59,9 @@
private static final int DEFAULT_DOT_SIZE = 100;
private static final float ALL_APPS_TABLET_MAX_ROWS = 5.5f;
+ public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
+ public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
+
// Ratio of empty space, qsb should take up to appear visually centered.
private final float mQsbCenterFactor;
@@ -204,7 +209,7 @@
public int overviewGridSideMargin;
// Widgets
- public final PointF appWidgetScale = new PointF(1.0f, 1.0f);
+ private final ViewScaleProvider mViewScaleProvider;
// Drop Target
public int dropTargetBarSizePx;
@@ -240,7 +245,8 @@
/** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
SparseArray<DotRenderer> dotRendererCache, boolean isMultiWindowMode,
- boolean transposeLayoutWithOrientation, boolean useTwoPanels, boolean isGestureMode) {
+ boolean transposeLayoutWithOrientation, boolean useTwoPanels, boolean isGestureMode,
+ @NonNull final ViewScaleProvider viewScaleProvider) {
this.inv = inv;
this.isLandscape = windowBounds.isLandscape();
@@ -473,6 +479,8 @@
flingToDeleteThresholdVelocity = res.getDimensionPixelSize(
R.dimen.drag_flingToDeleteMinVelocity);
+ mViewScaleProvider = viewScaleProvider;
+
// This is done last, after iconSizePx is calculated above.
mDotRendererWorkSpace = createDotRenderer(iconSizePx, dotRendererCache);
mDotRendererAllApps = createDotRenderer(allAppsIconSizePx, dotRendererCache);
@@ -669,13 +677,18 @@
.setMultiWindowMode(true)
.build();
- profile.hideWorkspaceLabelsIfNotEnoughSpace();
-
// We use these scales to measure and layout the widgets using their full invariant profile
// sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y;
- profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY);
+ if (appWidgetScaleX != 1 || appWidgetScaleY != 1) {
+ final PointF p = new PointF(appWidgetScaleX, appWidgetScaleY);
+ profile = profile.toBuilder(context)
+ .setViewScaleProvider(i -> p)
+ .build();
+ }
+
+ profile.hideWorkspaceLabelsIfNotEnoughSpace();
return profile;
}
@@ -1243,6 +1256,19 @@
}
/**
+ * Takes the View and return the scales of width and height depending on the DeviceProfile
+ * specifications
+ *
+ * @param itemInfo The tag of the widget view
+ * @return A PointF instance with the x set to be the scale of width, and y being the scale of
+ * height
+ */
+ @NonNull
+ public PointF getAppWidgetScale(@Nullable final ItemInfo itemInfo) {
+ return mViewScaleProvider.getScaleFromItemInfo(itemInfo);
+ }
+
+ /**
* @return the bounds for which the open folders should be contained within
*/
public Rect getAbsoluteOpenFolderBounds() {
@@ -1551,6 +1577,22 @@
}
}
+ /**
+ * Handler that deals with ItemInfo of the views for the DeviceProfile
+ */
+ @FunctionalInterface
+ public interface ViewScaleProvider {
+ /**
+ * Get the scales from the view
+ *
+ * @param itemInfo The tag of the widget view
+ * @return PointF instance containing the scale information, or null if using the default
+ * app widget scale of this device profile.
+ */
+ @NonNull
+ PointF getScaleFromItemInfo(@Nullable ItemInfo itemInfo);
+ }
+
public static class Builder {
private Context mContext;
private InvariantDeviceProfile mInv;
@@ -1562,6 +1604,7 @@
private boolean mIsMultiWindowMode = false;
private Boolean mTransposeLayoutWithOrientation;
private Boolean mIsGestureMode;
+ private ViewScaleProvider mViewScaleProvider = null;
private SparseArray<DotRenderer> mDotRendererCache;
@@ -1601,6 +1644,19 @@
return this;
}
+ /**
+ * Set the viewScaleProvider for the builder
+ *
+ * @param viewScaleProvider The viewScaleProvider to be set for the
+ * DeviceProfile
+ * @return This builder
+ */
+ @NonNull
+ public Builder setViewScaleProvider(@Nullable ViewScaleProvider viewScaleProvider) {
+ mViewScaleProvider = viewScaleProvider;
+ return this;
+ }
+
public DeviceProfile build() {
if (mWindowBounds == null) {
throw new IllegalArgumentException("Window bounds not set");
@@ -1614,9 +1670,12 @@
if (mDotRendererCache == null) {
mDotRendererCache = new SparseArray<>();
}
+ if (mViewScaleProvider == null) {
+ mViewScaleProvider = DEFAULT_PROVIDER;
+ }
return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds, mDotRendererCache,
mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels,
- mIsGestureMode);
+ mIsGestureMode, mViewScaleProvider);
}
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 5583eae..486a68f 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -25,6 +25,7 @@
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
@@ -32,6 +33,7 @@
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
@@ -109,8 +111,9 @@
if (child instanceof NavigableAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
((NavigableAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
+ final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag());
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
- profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpace, mTempRect);
+ appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect);
} else {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
mBorderSpace, null);
@@ -133,8 +136,9 @@
if (child instanceof NavigableAppWidgetHostView) {
((NavigableAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
+ final PointF appWidgetScale = dp.getAppWidgetScale((ItemInfo) child.getTag());
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
- dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpace, mTempRect);
+ appWidgetScale.x, appWidgetScale.y, mBorderSpace, mTempRect);
} else {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
mBorderSpace, null);
@@ -187,8 +191,9 @@
// Scale and center the widget to fit within its cells.
DeviceProfile profile = mActivity.getDeviceProfile();
- float scaleX = profile.appWidgetScale.x;
- float scaleY = profile.appWidgetScale.y;
+ final PointF appWidgetScale = profile.getAppWidgetScale((ItemInfo) child.getTag());
+ float scaleX = appWidgetScale.x;
+ float scaleY = appWidgetScale.y;
nahv.setScaleToFit(Math.min(scaleX, scaleY));
nahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2bc5e64..0eaad28 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -44,6 +44,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -369,7 +370,8 @@
float scale = 1;
if (isWidget) {
DeviceProfile profile = mLauncher.getDeviceProfile();
- scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+ final PointF appWidgetScale = profile.getAppWidgetScale(null);
+ scale = Utilities.shrinkRect(r, appWidgetScale.x, appWidgetScale.y);
}
size[0] = r.width();
size[1] = r.height();
@@ -2883,7 +2885,8 @@
r.top -= widgetPadding.top;
r.bottom += widgetPadding.bottom;
}
- Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
+ PointF appWidgetScale = profile.getAppWidgetScale(null);
+ Utilities.shrinkRect(r, appWidgetScale.x, appWidgetScale.y);
}
mTempFXY[0] = r.left;
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index a8546e8..c1bab54 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -20,6 +20,7 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.DeviceProfile.DEFAULT_SCALE;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
@@ -36,6 +37,7 @@
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
@@ -55,6 +57,7 @@
import android.view.WindowManager;
import android.widget.TextClock;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BubbleTextView;
@@ -100,6 +103,7 @@
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Collections;
@@ -173,6 +177,7 @@
private final Context mContext;
private final InvariantDeviceProfile mIdp;
private final DeviceProfile mDp;
+ private final DeviceProfile mDpOrig;
private final Rect mInsets;
private final WorkspaceItemInfo mWorkspaceItemInfo;
private final LayoutInflater mHomeElementInflater;
@@ -192,7 +197,16 @@
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
- mDp = idp.getDeviceProfile(context).copy(context);
+ mDp = idp.getDeviceProfile(context).toBuilder(context).setViewScaleProvider(
+ this::getAppWidgetScale).build();
+ if (context instanceof PreviewContext) {
+ Context tempContext = ((PreviewContext) context).getBaseContext();
+ mDpOrig = new InvariantDeviceProfile(tempContext, InvariantDeviceProfile
+ .getCurrentGridName(tempContext)).getDeviceProfile(tempContext)
+ .copy(tempContext);
+ } else {
+ mDpOrig = mDp;
+ }
WindowInsets currentWindowInsets = context.getSystemService(WindowManager.class)
.getCurrentWindowMetrics().getWindowInsets();
@@ -390,6 +404,41 @@
addInScreenFromBind(view, info);
}
+ @NonNull
+ private PointF getAppWidgetScale(@Nullable ItemInfo itemInfo) {
+ if (!(itemInfo instanceof LauncherAppWidgetInfo)) {
+ return DEFAULT_SCALE;
+ }
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) itemInfo;
+ final Size launcherWidgetSize = mLauncherWidgetSpanInfo.get(info.appWidgetId);
+ if (launcherWidgetSize == null) {
+ return DEFAULT_SCALE;
+ }
+ final Size origSize = WidgetSizes.getWidgetSizePx(mDpOrig,
+ launcherWidgetSize.getWidth(), launcherWidgetSize.getHeight());
+ final Size newSize = WidgetSizes.getWidgetSizePx(mDp, info.spanX, info.spanY);
+ final Rect previewInset = new Rect();
+ final Rect origInset = new Rect();
+ // When the setup() is called for the LayoutParams, insets are added to the width
+ // and height of the view. This is not accounted for in WidgetSizes and is handled
+ // here.
+ if (mDp.shouldInsetWidgets()) {
+ previewInset.set(mDp.inv.defaultWidgetPadding);
+ } else {
+ previewInset.setEmpty();
+ }
+ if (mDpOrig.shouldInsetWidgets()) {
+ origInset.set(mDpOrig.inv.defaultWidgetPadding);
+ } else {
+ origInset.setEmpty();
+ }
+
+ return new PointF((float) newSize.getWidth() / (origSize.getWidth()
+ + origInset.left + origInset.right),
+ (float) newSize.getHeight() / (origSize.getHeight()
+ + origInset.top + origInset.bottom));
+ }
+
private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
CellLayout screen = mWorkspaceScreens.get(info.screenId);
View view = PredictedAppIconInflater.inflate(mHomeElementInflater, screen, info);