diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index f27eb7995b..596b15ec57 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -38,11 +38,13 @@ import android.util.SparseArray; 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 @@ public class DeviceProfile { 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 class DeviceProfile { 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 @@ public class DeviceProfile { /** TODO: Once we fully migrate to staged split, remove "isMultiWindowMode" */ DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds, SparseArray 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 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { .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; } @@ -1242,6 +1255,19 @@ public class DeviceProfile { + getOverviewActionsClaimedSpaceBelow(); } + /** + * 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 */ @@ -1551,6 +1577,22 @@ public class DeviceProfile { } } + /** + * 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 @@ public class DeviceProfile { private boolean mIsMultiWindowMode = false; private Boolean mTransposeLayoutWithOrientation; private Boolean mIsGestureMode; + private ViewScaleProvider mViewScaleProvider = null; private SparseArray mDotRendererCache; @@ -1601,6 +1644,19 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { 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 5583eaeba9..486a68f691 100644 --- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java +++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java @@ -25,6 +25,7 @@ import static com.android.launcher3.CellLayout.WORKSPACE; 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 android.view.ViewGroup; 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 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. 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 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. 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 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon. // 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 2bc5e646a0..0eaad28d96 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -44,6 +44,7 @@ import android.content.Context; 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 @@ public class Workspace extends PagedView 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 @@ public class Workspace extends PagedView 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 a8546e8448..c1bab544cb 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.EXACTLY; 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.ContextWrapper; 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.WindowInsets; 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.LauncherAppWidgetProviderInfo; 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 @@ public class LauncherPreviewRenderer extends ContextWrapper 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 @@ public class LauncherPreviewRenderer extends ContextWrapper 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 @@ public class LauncherPreviewRenderer extends ContextWrapper 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); diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt index cf6be7fb58..77ade80d54 100644 --- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt +++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt @@ -20,6 +20,7 @@ import android.graphics.PointF import android.graphics.Rect import android.util.SparseArray import androidx.test.core.app.ApplicationProvider +import com.android.launcher3.DeviceProfile.DEFAULT_PROVIDER; import com.android.launcher3.util.DisplayController.Info import com.android.launcher3.util.WindowBounds import org.junit.Before @@ -61,7 +62,8 @@ abstract class DeviceProfileBaseTest { isMultiWindowMode, transposeLayoutWithOrientation, useTwoPanels, - isGestureMode + isGestureMode, + DEFAULT_PROVIDER ) protected fun initializeVarsForPhone(isGestureMode: Boolean = true,