Adding support for determining split layout for launcher.
> Simulating the windo wmanager API to get available device
profiles until final API
> When a device has multiple internal displays, and with both
tablet and phone possibilities, it uses a split workspace layout
Bug: 186160341
Bug: 175782275
Test: Manual
Change-Id: Ieff2329acac7cdd6b9abe6f96cd459cd45bd0efe
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 5d41bb5..20b4631 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,5 +1,7 @@
package com.android.launcher3;
+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;
@@ -10,7 +12,9 @@
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;
@@ -25,16 +29,14 @@
import android.widget.ImageButton;
import android.widget.ImageView;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
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;
@@ -43,22 +45,6 @@
private static final Rect sTmpRect = new Rect();
- // Represents the cell size on the grid in the two orientations.
- public static final MainThreadInitializedObject<Point[]> CELL_SIZE =
- new MainThreadInitializedObject<>(c -> {
- InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
- return new Point[] {inv.landscapeProfile.getCellSize(),
- inv.portraitProfile.getCellSize()};
- });
-
- // Represents the border spacing size on the grid in the two orientations.
- public static final MainThreadInitializedObject<int[]> BORDER_SPACING_SIZE =
- new MainThreadInitializedObject<>(c -> {
- InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
- return new int[] {inv.landscapeProfile.cellLayoutBorderSpacingPx,
- inv.portraitProfile.cellLayoutBorderSpacingPx};
- });
-
private static final int HANDLE_COUNT = 4;
private static final int INDEX_LEFT = 0;
private static final int INDEX_TOP = 1;
@@ -202,7 +188,7 @@
mMaxHSpan = info.maxSpanX;
mMaxVSpan = info.maxSpanY;
- mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
+ mWidgetPadding = getDefaultPaddingForWidget(getContext(),
widgetView.getAppWidgetInfo().provider, null);
if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
@@ -392,81 +378,82 @@
mWidgetView.requestLayout();
}
- public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
- int spanX, int spanY) {
- List<SizeF> sizes = getWidgetSizes(launcher, spanX, spanY);
+ public static void updateWidgetSizeRanges(
+ AppWidgetHostView widgetView, Context context, int spanX, int spanY) {
+ List<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
if (ATLEAST_S) {
widgetView.updateAppWidgetSize(new Bundle(), sizes);
} else {
- Rect bounds = getMinMaxSizes(sizes, null /* outRect */);
+ Rect bounds = getMinMaxSizes(sizes);
widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
bounds.bottom);
}
}
- private static SizeF getWidgetSize(Context context, Point cellSize, int spanX, int spanY,
- int borderSpacing) {
- final float density = context.getResources().getDisplayMetrics().density;
- final float hBorderSpacing = (spanX - 1) * borderSpacing;
- final float vBorderSpacing = (spanY - 1) * borderSpacing;
-
- return new SizeF(((spanX * cellSize.x) + hBorderSpacing) / density,
- ((spanY * cellSize.y) + vBorderSpacing) / density);
- }
-
/** Returns the list of sizes for a widget of given span, in dp. */
public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) {
- final Point[] cellSize = CELL_SIZE.get(context);
- final int[] borderSpacing = BORDER_SPACING_SIZE.get(context);
-
- SizeF landSize = getWidgetSize(context, cellSize[0], spanX, spanY, borderSpacing[0]);
- SizeF portSize = getWidgetSize(context, cellSize[1], spanX, spanY, borderSpacing[1]);
-
ArrayList<SizeF> sizes = new ArrayList<>(2);
- sizes.add(landSize);
- sizes.add(portSize);
+ 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<SizeF> 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<SizeF> 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.
- * @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<SizeF> sizes, @Nullable Rect outRect) {
- if (outRect == null) {
- outRect = new Rect();
- }
+ private static Rect getMinMaxSizes(List<SizeF> sizes) {
if (sizes.isEmpty()) {
- outRect.set(0, 0, 0, 0);
+ return new Rect();
} else {
SizeF first = sizes.get(0);
- outRect.set((int) first.getWidth(), (int) first.getHeight(), (int) first.getWidth(),
- (int) first.getHeight());
+ Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(),
+ (int) first.getWidth(), (int) first.getHeight());
for (int i = 1; i < sizes.size(); i++) {
- outRect.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
+ result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
}
+ return result;
}
- 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/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index fd97936..d5860dc 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -31,7 +31,6 @@
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.util.DisplayMetrics;
-import android.util.Log;
import android.view.Surface;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -42,7 +41,6 @@
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.WindowBounds;
@@ -52,9 +50,6 @@
@SuppressLint("NewApi")
public class DeviceProfile {
- private static final float TABLET_MIN_DPS = 600;
- private static final float LARGE_TABLET_MIN_DPS = 720;
-
private static final int DEFAULT_DOT_SIZE = 100;
public final InvariantDeviceProfile inv;
@@ -63,9 +58,9 @@
// Device properties
public final boolean isTablet;
- public final boolean isLargeTablet;
public final boolean isPhone;
public final boolean transposeLayoutWithOrientation;
+ public final boolean isTwoPanels;
// Device properties in current orientation
public final boolean isLandscape;
@@ -164,6 +159,7 @@
public int allAppsCellWidthPx;
public int allAppsIconSizePx;
public int allAppsIconDrawablePaddingPx;
+ public final int numShownAllAppsColumns;
public float allAppsIconTextSizePx;
// Overview
@@ -194,42 +190,30 @@
// How much of the bottom inset is due to Taskbar rather than other system elements.
public int nonOverlappingTaskbarInset;
- DeviceProfile(Context context, InvariantDeviceProfile inv, Info info,
- Point minSize, Point maxSize, int width, int height, boolean isLandscape,
+ DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
- Point windowPosition) {
+ boolean useTwoPanels) {
this.inv = inv;
- this.isLandscape = isLandscape;
+ this.isLandscape = windowBounds.isLandscape();
this.isMultiWindowMode = isMultiWindowMode;
this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
- windowX = windowPosition.x;
- windowY = windowPosition.y;
+ windowX = windowBounds.bounds.left;
+ windowY = windowBounds.bounds.top;
isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
// Determine sizes.
- widthPx = width;
- heightPx = height;
- int nonFinalAvailableHeightPx;
- if (isLandscape) {
- availableWidthPx = maxSize.x;
- nonFinalAvailableHeightPx = minSize.y;
- } else {
- availableWidthPx = minSize.x;
- nonFinalAvailableHeightPx = maxSize.y;
- }
+ widthPx = windowBounds.bounds.width();
+ heightPx = windowBounds.bounds.height();
+ availableWidthPx = windowBounds.availableSize.x;
+ int nonFinalAvailableHeightPx = windowBounds.availableSize.y;
mInfo = info;
+ isTablet = info.isTablet(windowBounds);
+ isPhone = !isTablet;
+ isTwoPanels = isTablet && useTwoPanels;
- // Constants from resources
- float swDPs = dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y),
- info.densityDpi);
- boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation);
- // Tablet UI is built with assumption that simulated landscape is disabled.
- isTablet = allowRotation && swDPs >= TABLET_MIN_DPS;
- isLargeTablet = isTablet && swDPs >= LARGE_TABLET_MIN_DPS;
- isPhone = !isTablet && !isLargeTablet;
aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@@ -284,7 +268,7 @@
? 0
: res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
- if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get() && isTablet) {
+ if (isTwoPanels) {
cellLayoutPaddingLeftRightPx =
res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
cellLayoutBottomPaddingPx = 0;
@@ -309,7 +293,10 @@
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
- numShownHotseatIcons = inv.numShownHotseatIcons;
+ numShownHotseatIcons =
+ isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+ numShownAllAppsColumns =
+ isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
hotseatBarTopPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
hotseatBarBottomPaddingPx = (isTallDevice ? 0
@@ -393,11 +380,12 @@
}
public Builder toBuilder(Context context) {
- Point size = new Point(availableWidthPx, availableHeightPx);
+ WindowBounds bounds =
+ new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx);
+ bounds.bounds.offsetTo(windowX, windowY);
return new Builder(context, inv, mInfo)
- .setSizeRange(size, size)
- .setSize(widthPx, heightPx)
- .setWindowPosition(windowX, windowY)
+ .setWindowBounds(bounds)
+ .setUseTwoPanels(isTwoPanels)
.setMultiWindowMode(isMultiWindowMode);
}
@@ -409,15 +397,8 @@
* TODO: Move this to the builder as part of setMultiWindowMode
*/
public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) {
- // We take the minimum sizes of this profile and it's multi-window variant to ensure that
- // the system decor is always excluded.
- Point mwSize = new Point(Math.min(availableWidthPx, windowBounds.availableSize.x),
- Math.min(availableHeightPx, windowBounds.availableSize.y));
-
DeviceProfile profile = toBuilder(context)
- .setSizeRange(mwSize, mwSize)
- .setSize(windowBounds.bounds.width(), windowBounds.bounds.height())
- .setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top)
+ .setWindowBounds(windowBounds)
.setMultiWindowMode(true)
.build();
@@ -434,14 +415,6 @@
}
/**
- * Inverse of {@link #getMultiWindowProfile(Context, WindowBounds)}
- * @return device profile corresponding to the current orientation in non multi-window mode.
- */
- public DeviceProfile getFullScreenProfile() {
- return isLandscape ? inv.landscapeProfile : inv.portraitProfile;
- }
-
- /**
* Checks if there is enough space for labels on the workspace.
* If there is not, labels on the Workspace are hidden.
* It is important to call this method after the All Apps variables have been set.
@@ -553,7 +526,7 @@
}
// All apps
- if (allAppsHasDifferentNumColumns()) {
+ if (numShownAllAppsColumns != inv.numColumns) {
allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics);
allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mMetrics);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
@@ -667,18 +640,20 @@
}
public Point getCellSize() {
- return getCellSize(inv.numColumns, inv.numRows);
+ return getCellSize(null);
}
- private Point getCellSize(int numColumns, int numRows) {
- Point result = new Point();
+ public Point getCellSize(Point result) {
+ if (result == null) {
+ result = new Point();
+ }
// Since we are only concerned with the overall padding, layout direction does
// not matter.
Point padding = getTotalWorkspacePadding();
result.x = calculateCellWidth(availableWidthPx - padding.x
- - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, numColumns);
+ - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y
- - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, numRows);
+ - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
return result;
}
@@ -723,7 +698,7 @@
padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
- if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get()) {
+ if (isTwoPanels) {
padding.set(0, padding.top, 0, padding.bottom);
}
} else {
@@ -751,7 +726,7 @@
// for this, we pad the left and right of the hotseat with half of the difference of a
// workspace cell vs a hotseat cell.
float workspaceCellWidth = (float) widthPx / inv.numColumns;
- float hotseatCellWidth = (float) widthPx / inv.numShownHotseatIcons;
+ float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
mHotseatPadding.set(
hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
@@ -802,13 +777,6 @@
}
/**
- * Returns true when the number of workspace columns and all apps columns differs.
- */
- private boolean allAppsHasDifferentNumColumns() {
- return inv.numAllAppsColumns != inv.numColumns;
- }
-
- /**
* Updates orientation information and returns true if it has changed from the previous value.
*/
public boolean updateIsSeascape(Context context) {
@@ -828,7 +796,7 @@
}
public boolean shouldFadeAdjacentWorkspaceScreens() {
- return isVerticalBarLayout() || isLargeTablet;
+ return isVerticalBarLayout();
}
public int getCellHeight(@ContainerType int containerType) {
@@ -854,13 +822,13 @@
writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
writer.println(prefix + "\tisTablet:" + isTablet);
- writer.println(prefix + "\tisLargeTablet:" + isLargeTablet);
writer.println(prefix + "\tisPhone:" + isPhone);
writer.println(prefix + "\ttransposeLayoutWithOrientation:"
+ transposeLayoutWithOrientation);
writer.println(prefix + "\tisLandscape:" + isLandscape);
writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
+ writer.println(prefix + "\tisTwoPanels:" + isTwoPanels);
writer.println(prefix + pxToDpStr("windowX", windowX));
writer.println(prefix + pxToDpStr("windowY", windowY));
@@ -907,6 +875,7 @@
writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
allAppsIconDrawablePaddingPx));
writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
+ writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
@@ -916,6 +885,7 @@
hotseatBarSidePaddingStartPx));
writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
hotseatBarSidePaddingEndPx));
+ writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
@@ -967,41 +937,16 @@
private InvariantDeviceProfile mInv;
private Info mInfo;
- private final Point mWindowPosition = new Point();
- private Point mMinSize, mMaxSize;
- private int mWidth, mHeight;
+ private WindowBounds mWindowBounds;
+ private boolean mUseTwoPanels;
- private boolean mIsLandscape;
private boolean mIsMultiWindowMode = false;
- private boolean mTransposeLayoutWithOrientation;
+ private Boolean mTransposeLayoutWithOrientation;
public Builder(Context context, InvariantDeviceProfile inv, Info info) {
mContext = context;
mInv = inv;
mInfo = info;
- mTransposeLayoutWithOrientation = context.getResources()
- .getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
- "transposeLayout=" + mTransposeLayoutWithOrientation);
- }
- }
-
- public Builder setSizeRange(Point minSize, Point maxSize) {
- mMinSize = minSize;
- mMaxSize = maxSize;
- return this;
- }
-
- public Builder setSize(int width, int height) {
- mWidth = width;
- mHeight = height;
- mIsLandscape = mWidth > mHeight;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
- "isLandscape=" + mIsLandscape + " w=" + mWidth + " h=" + mHeight);
- }
- return this;
}
public Builder setMultiWindowMode(boolean isMultiWindowMode) {
@@ -1009,11 +954,14 @@
return this;
}
- /**
- * Sets the window position if not full-screen
- */
- public Builder setWindowPosition(int x, int y) {
- mWindowPosition.set(x, y);
+ public Builder setUseTwoPanels(boolean useTwoPanels) {
+ mUseTwoPanels = useTwoPanels;
+ return this;
+ }
+
+
+ public Builder setWindowBounds(WindowBounds bounds) {
+ mWindowBounds = bounds;
return this;
}
@@ -1023,9 +971,14 @@
}
public DeviceProfile build() {
- return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize,
- mWidth, mHeight, mIsLandscape, mIsMultiWindowMode,
- mTransposeLayoutWithOrientation, mWindowPosition);
+ if (mWindowBounds == null) {
+ throw new IllegalArgumentException("Window bounds not set");
+ }
+ if (mTransposeLayoutWithOrientation == null) {
+ mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
+ }
+ return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds,
+ mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels);
}
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 8496fd5..b2a9e75 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -90,11 +90,11 @@
public void resetLayout(boolean hasVerticalHotseat) {
removeAllViewsInLayout();
mHasVerticalHotseat = hasVerticalHotseat;
- InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv;
+ DeviceProfile dp = mActivity.getDeviceProfile();
if (hasVerticalHotseat) {
- setGridSize(1, idp.numShownHotseatIcons);
+ setGridSize(1, dp.numShownHotseatIcons);
} else {
- setGridSize(idp.numShownHotseatIcons, 1);
+ setGridSize(dp.numShownHotseatIcons, 1);
}
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 1332e14..b263d38 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,11 +16,12 @@
package com.android.launcher3;
+import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
-import static com.android.launcher3.util.DisplayController.CHANGE_SIZE;
+import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
@@ -55,6 +56,7 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.WindowBounds;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -62,6 +64,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
public class InvariantDeviceProfile {
@@ -73,6 +76,9 @@
public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
+ private static final int DEFAULT_TRUE = -1;
+ private static final int DEFAULT_SPLIT_DISPLAY = 2;
+
private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
@@ -136,6 +142,7 @@
* Number of columns in the all apps list.
*/
public int numAllAppsColumns;
+ public int numDatabaseAllAppsColumns;
/**
* Do not query directly. see {@link DeviceProfile#isScalableGrid}.
@@ -147,8 +154,7 @@
public int defaultLayoutId;
int demoModeLayoutId;
- public DeviceProfile landscapeProfile;
- public DeviceProfile portraitProfile;
+ public final List<DeviceProfile> supportedProfiles = new ArrayList<>();
@Nullable public DevicePaddings devicePaddings;
@@ -175,6 +181,7 @@
numShownHotseatIcons = p.numShownHotseatIcons;
numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
+ numDatabaseAllAppsColumns = p.numDatabaseAllAppsColumns;
isScalable = p.isScalable;
devicePaddingId = p.devicePaddingId;
minCellHeight = p.minCellHeight;
@@ -204,7 +211,7 @@
DisplayController.INSTANCE.get(context).addChangeListener(
(displayContext, info, flags) -> {
- if ((flags & (CHANGE_SIZE | CHANGE_DENSITY)) != 0) {
+ if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
onConfigChanged(displayContext);
}
});
@@ -232,35 +239,29 @@
// Get the display info based on default display and interpolate it to existing display
DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
DisplayController.INSTANCE.get(context).getInfo(),
- getPredefinedDeviceProfiles(context, gridName));
+ getPredefinedDeviceProfiles(context, gridName, false), false);
Info myInfo = new Info(context, display);
DisplayOption myDisplayOption = invDistWeightedInterpolate(
- myInfo, getPredefinedDeviceProfiles(context, gridName));
+ myInfo, getPredefinedDeviceProfiles(context, gridName, false), false);
DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
.add(myDisplayOption);
result.iconSize = defaultDisplayOption.iconSize;
result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
- result.numShownHotseatIcons = myDisplayOption.numShownHotseatIcons;
if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
- result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns;
} else {
result.allAppsIconSize = myDisplayOption.allAppsIconSize;
- result.numAllAppsColumns = myDisplayOption.numAllAppsColumns;
}
result.minCellHeight = defaultDisplayOption.minCellHeight;
result.minCellWidth = defaultDisplayOption.minCellWidth;
result.borderSpacing = defaultDisplayOption.borderSpacing;
- initGrid(context, myInfo, result);
+ initGrid(context, myInfo, result, false);
}
public static String getCurrentGridName(Context context) {
- if (ENABLE_TWO_PANEL_HOME.get()) {
- return ENABLE_TWO_PANEL_HOME.key;
- }
return Utilities.isGridOptionsEnabled(context)
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
}
@@ -278,20 +279,33 @@
private String initGrid(Context context, String gridName) {
Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
- ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
+ // Determine if we have split display
- DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
- initGrid(context, displayInfo, displayOption);
+ boolean isTablet = false, isPhone = false;
+ for (WindowBounds bounds : displayInfo.supportedBounds) {
+ if (displayInfo.isTablet(bounds)) {
+ isTablet = true;
+ } else {
+ isPhone = true;
+ }
+ }
+ boolean isSplitDisplay = isPhone && isTablet && ENABLE_TWO_PANEL_HOME.get();
+
+ ArrayList<DisplayOption> allOptions =
+ getPredefinedDeviceProfiles(context, gridName, isSplitDisplay);
+ DisplayOption displayOption =
+ invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay);
+ initGrid(context, displayInfo, displayOption, isSplitDisplay);
return displayOption.grid.name;
}
private void initGrid(
- Context context, Info displayInfo, DisplayOption displayOption) {
+ Context context, Info displayInfo, DisplayOption displayOption,
+ boolean isSplitDisplay) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
- numDatabaseHotseatIcons = closestProfile.numDatabaseHotseatIcons;
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -313,8 +327,14 @@
minCellHeight = displayOption.minCellHeight;
minCellWidth = displayOption.minCellWidth;
borderSpacing = displayOption.borderSpacing;
- numShownHotseatIcons = Math.round(displayOption.numShownHotseatIcons);
- numAllAppsColumns = Math.round(displayOption.numAllAppsColumns);
+
+ numShownHotseatIcons = closestProfile.numHotseatIcons;
+ numDatabaseHotseatIcons = isSplitDisplay
+ ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
+
+ numAllAppsColumns = closestProfile.numAllAppsColumns;
+ numDatabaseAllAppsColumns = isSplitDisplay
+ ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
if (Utilities.isGridOptionsEnabled(context)) {
allAppsIconSize = displayOption.allAppsIconSize;
@@ -332,31 +352,26 @@
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, metrics);
- Point realSize = new Point(displayInfo.realSize);
- // The real size never changes. smallSide and largeSide will remain the
- // same in any orientation.
- int smallSide = Math.min(realSize.x, realSize.y);
- int largeSide = Math.max(realSize.x, realSize.y);
+ supportedProfiles.clear();
+ defaultWallpaperSize = new Point(displayInfo.currentSize);
+ for (WindowBounds bounds : displayInfo.supportedBounds) {
+ supportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
+ .setUseTwoPanels(isSplitDisplay)
+ .setWindowBounds(bounds).build());
- DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo)
- .setSizeRange(new Point(displayInfo.smallestSize),
- new Point(displayInfo.largestSize));
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
- "largeSide=" + largeSide + " smallSide=" + smallSide);
- }
+ // Wallpaper size should be the maximum of the all possible sizes Launcher expects
+ int displayWidth = bounds.bounds.width();
+ int displayHeight = bounds.bounds.height();
+ defaultWallpaperSize.y = Math.max(defaultWallpaperSize.y, displayHeight);
- landscapeProfile = builder.setSize(largeSide, smallSide).build();
- portraitProfile = builder.setSize(smallSide, largeSide).build();
-
- // We need to ensure that there is enough extra space in the wallpaper
- // for the intended parallax effects
- if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) {
- defaultWallpaperSize = new Point(
- (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)),
- largeSide);
- } else {
- defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
+ // We need to ensure that there is enough extra space in the wallpaper
+ // for the intended parallax effects
+ float parallaxFactor =
+ dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720
+ ? 2
+ : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight);
+ defaultWallpaperSize.x =
+ Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth));
}
ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
@@ -385,7 +400,7 @@
} else if (!savedIconMaskPath.equals(getIconShapePath(context))) {
getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
.apply();
- apply(context, CHANGE_FLAG_ICON_PARAMS);
+ apply(CHANGE_FLAG_ICON_PARAMS);
}
}
@@ -423,16 +438,17 @@
IconShape.init(context);
}
- apply(context, changeFlags);
+ apply(changeFlags);
}
- private void apply(Context context, int changeFlags) {
+ private void apply(int changeFlags) {
for (OnIDPChangeListener listener : mChangeListeners) {
listener.onIdpChanged(changeFlags, this);
}
}
- static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
+ private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(
+ Context context, String gridName, boolean isSplitDisplay) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
@@ -449,8 +465,9 @@
&& type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) && "display-option".equals(
parser.getName())) {
- profiles.add(new DisplayOption(
- gridOption, context, Xml.asAttributeSet(parser)));
+ profiles.add(new DisplayOption(gridOption, context,
+ Xml.asAttributeSet(parser),
+ isSplitDisplay ? DEFAULT_SPLIT_DISPLAY : DEFAULT_TRUE));
}
}
}
@@ -521,17 +538,29 @@
return (float) Math.hypot(x1 - x0, y1 - y0);
}
- @VisibleForTesting
- static DisplayOption invDistWeightedInterpolate(
- Info displayInfo, ArrayList<DisplayOption> points) {
- Point smallestSize = new Point(displayInfo.smallestSize);
- Point largestSize = new Point(displayInfo.largestSize);
+ private static DisplayOption invDistWeightedInterpolate(
+ Info displayInfo, ArrayList<DisplayOption> points, boolean isSplitDisplay) {
+ int minWidthPx = Integer.MAX_VALUE;
+ int minHeightPx = Integer.MAX_VALUE;
+ for (WindowBounds bounds : displayInfo.supportedBounds) {
+ boolean isTablet = displayInfo.isTablet(bounds);
+ if (isTablet && isSplitDisplay) {
+ // For split displays, take half width per page
+ minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
+ minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
- // This guarantees that width < height
- float width = Utilities.dpiFromPx((float) Math.min(smallestSize.x, smallestSize.y),
- displayInfo.densityDpi);
- float height = Utilities.dpiFromPx((float) Math.min(largestSize.x, largestSize.y),
- displayInfo.densityDpi);
+ } else if (!isTablet && bounds.isLandscape()) {
+ // We will use transposed layout in this case
+ minWidthPx = Math.min(minWidthPx, bounds.availableSize.y);
+ minHeightPx = Math.min(minHeightPx, bounds.availableSize.x);
+ } else {
+ minWidthPx = Math.min(minWidthPx, bounds.availableSize.x);
+ minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
+ }
+ }
+
+ float width = dpiFromPx(minWidthPx, displayInfo.densityDpi);
+ float height = dpiFromPx(minHeightPx, displayInfo.densityDpi);
// Sort the profiles based on the closeness to the device size
Collections.sort(points, (a, b) ->
@@ -556,33 +585,30 @@
return out.multiply(1.0f / weights);
}
- @VisibleForTesting
- static DisplayOption invDistWeightedInterpolate(float width, float height,
- ArrayList<DisplayOption> points) {
- float weights = 0;
-
- DisplayOption p = points.get(0);
- if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
- return p;
- }
-
- DisplayOption out = new DisplayOption();
- for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
- p = points.get(i);
- float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
- weights += w;
- out.add(new DisplayOption().add(p).multiply(w));
- }
- return out.multiply(1.0f / weights);
- }
-
public DeviceProfile getDeviceProfile(Context context) {
+ Resources res = context.getResources();
+ Configuration config = context.getResources().getConfiguration();
+
+ float availableWidth = config.screenWidthDp * res.getDisplayMetrics().density;
+ float availableHeight = config.screenHeightDp * res.getDisplayMetrics().density;
+
if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "getDeviceProfile: orientation="
- + context.getResources().getConfiguration().orientation);
+ Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
+ "getDeviceProfile: orientation=" + config.orientation
+ + " size=" + availableWidth + "x" + availableHeight);
}
- return context.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile;
+ DeviceProfile bestMatch = supportedProfiles.get(0);
+ float minDiff = Float.MAX_VALUE;
+
+ for (DeviceProfile profile : supportedProfiles) {
+ float diff = Math.abs(profile.availableWidthPx - availableWidth)
+ + Math.abs(profile.availableHeightPx - availableHeight);
+ if (diff < minDiff) {
+ minDiff = diff;
+ bestMatch = profile;
+ }
+ }
+ return bestMatch;
}
private static float weight(float x0, float y0, float x1, float y1, float pow) {
@@ -639,6 +665,9 @@
private final int numFolderRows;
private final int numFolderColumns;
+ private final int numAllAppsColumns;
+ private final int numDatabaseAllAppsColumns;
+ private final int numHotseatIcons;
private final int numDatabaseHotseatIcons;
private final String dbFile;
@@ -649,8 +678,6 @@
private final boolean isScalable;
private final int devicePaddingId;
- public final boolean visible;
-
private final SparseArray<TypedValue> extraAttrs;
public GridOption(Context context, AttributeSet attrs) {
@@ -665,8 +692,17 @@
R.styleable.GridDisplayOption_defaultLayoutId, 0);
demoModeLayoutId = a.getResourceId(
R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
- numDatabaseHotseatIcons = a.getInt(
+
+ numAllAppsColumns = a.getInt(
+ R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
+ numDatabaseAllAppsColumns = a.getInt(
+ R.styleable.GridDisplayOption_numExtendedAllAppsColumns, 2 * numAllAppsColumns);
+
+ numHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
+ numDatabaseHotseatIcons = a.getInt(
+ R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
+
numFolderRows = a.getInt(
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
@@ -677,24 +713,21 @@
devicePaddingId = a.getResourceId(
R.styleable.GridDisplayOption_devicePaddingId, 0);
- visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true);
-
a.recycle();
-
extraAttrs = Themes.createValueMap(context, attrs,
IntArray.wrap(R.styleable.GridDisplayOption));
}
}
- private static final class DisplayOption {
- private final GridOption grid;
+ @VisibleForTesting
+ static final class DisplayOption {
+
+ public final GridOption grid;
private final float minWidthDps;
private final float minHeightDps;
private final boolean canBeDefault;
- private float numShownHotseatIcons;
- private float numAllAppsColumns;
private float minCellHeight;
private float minCellWidth;
private float borderSpacing;
@@ -706,7 +739,7 @@
private float allAppsIconSize;
private float allAppsIconTextSize;
- DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
+ DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) {
this.grid = grid;
TypedArray a = context.obtainStyledAttributes(
@@ -714,12 +747,9 @@
minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
- canBeDefault = a.getBoolean(
- R.styleable.ProfileDisplayOption_canBeDefault, false);
- numShownHotseatIcons = a.getInt(R.styleable.ProfileDisplayOption_numShownHotseatIcons,
- grid.numDatabaseHotseatIcons);
- numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns,
- grid.numColumns);
+
+ canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0)
+ == defaultFlagValue;
minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
@@ -748,16 +778,12 @@
minWidthDps = 0;
minHeightDps = 0;
canBeDefault = false;
- numShownHotseatIcons = 0;
- numAllAppsColumns = 0;
minCellHeight = 0;
minCellWidth = 0;
borderSpacing = 0;
}
private DisplayOption multiply(float w) {
- numShownHotseatIcons *= w;
- numAllAppsColumns *= w;
iconSize *= w;
landscapeIconSize *= w;
allAppsIconSize *= w;
@@ -771,8 +797,6 @@
}
private DisplayOption add(DisplayOption p) {
- numShownHotseatIcons += p.numShownHotseatIcons;
- numAllAppsColumns += p.numAllAppsColumns;
iconSize += p.iconSize;
landscapeIconSize += p.landscapeIconSize;
allAppsIconSize += p.allAppsIconSize;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f640118..09c7b7a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -189,7 +189,6 @@
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler;
-import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -2332,8 +2331,7 @@
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
- Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this,
- pendingInfo);
+ Bundle options = pendingInfo.getDefaultSizeOptions(this);
boolean isDirectConfig =
item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 05d6e04..7c5f99e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -456,7 +456,7 @@
}
private boolean isTwoPanelEnabled() {
- return mLauncher.mDeviceProfile.isTablet && FeatureFlags.ENABLE_TWO_PANEL_HOME.get();
+ return mLauncher.mDeviceProfile.isTwoPanels;
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 40f7ab1..119a91f 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -157,7 +157,7 @@
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {
- holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns);
+ holder.adapter.setAppsPerRow(dp.numShownAllAppsColumns);
if (holder.recyclerView != null) {
// Remove all views and clear the pool, while keeping the data same. After this
// call, all the viewHolders will be recreated.
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 5b4c4c5..70588ea 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -283,7 +283,7 @@
mOnIconClickListener = launcher.getItemOnClickListener();
mSearchAdapterProvider = searchAdapterProvider;
- setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
+ setAppsPerRow(mLauncher.getDeviceProfile().numShownAllAppsColumns);
}
public void setAppsPerRow(int appsPerRow) {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index d5a04a6..5ba36f2 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -64,7 +64,6 @@
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetCellPreview;
-import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -234,7 +233,7 @@
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
- mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+ mWidgetOptions = pendingInfo.getDefaultSizeOptions(this);
mWidgetCell.getWidgetView().setTag(pendingInfo);
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 911f8c3..cb42e7a 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -90,10 +90,7 @@
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& GridOption.TAG_NAME.equals(parser.getName())) {
- GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser));
- if (option.visible) {
- result.add(option);
- }
+ result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
}
}
} catch (IOException | XmlPullParserException e) {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 31764c5..6a8ac64 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -224,7 +224,7 @@
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
- mDp = idp.portraitProfile.copy(context);
+ mDp = idp.getDeviceProfile(context).copy(context);
mMigrated = migrated;
// TODO: get correct insets once display cutout API is available.
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index a191df4..22c3f58 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -20,7 +20,7 @@
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 static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions;
import android.app.Activity;
import android.app.Fragment;
@@ -32,11 +32,9 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Rect;
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
-import android.util.SizeF;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -45,7 +43,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -53,8 +50,6 @@
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)}.
@@ -163,7 +158,7 @@
protected String mKeyWidgetId = "qsb_widget_id";
private QsbWidgetHost mQsbWidgetHost;
- private AppWidgetProviderInfo mWidgetInfo;
+ protected AppWidgetProviderInfo mWidgetInfo;
private QsbWidgetHostView mQsb;
// We need to store the orientation here, due to a bug (b/64916689) that results in widgets
@@ -297,19 +292,7 @@
protected Bundle createBindOptions() {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
-
- Bundle opts = new Bundle();
- ArrayList<SizeF> 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;
+ return getWidgetSizeOptions(getContext(), mWidgetInfo.provider, idp.numColumns, 1);
}
protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index f5e74b7..5999091 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -87,8 +87,8 @@
if (mDragLayer != null) {
return;
}
- InvariantDeviceProfile currentDisplayIdp =
- new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay());
+ InvariantDeviceProfile currentDisplayIdp = new InvariantDeviceProfile(
+ this, getWindow().getDecorView().getDisplay());
// Disable transpose layout and use multi-window mode so that the icons are scaled properly
mDeviceProfile = currentDisplayIdp.getDeviceProfile(this)
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 40630d3..f78f6dd 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,7 +29,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.model.data.ItemInfo;
@@ -109,8 +108,6 @@
setMeasuredDimension(width, height);
DeviceProfile grid = mActivity.getDeviceProfile();
- InvariantDeviceProfile idp = grid.inv;
-
int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
@@ -118,10 +115,10 @@
int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
+ grid.cellLayoutPaddingLeftRightPx);
- int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
+ int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding;
int appsWidth = Math.min(width, maxWidth);
- int maxHeight = grid.allAppsCellHeightPx * idp.numAllAppsColumns + padding;
+ int maxHeight = grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + padding;
int appsHeight = Math.min(height, maxHeight);
mAppsView.measure(
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 75c089e..b751207 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -18,8 +18,10 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -32,16 +34,22 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Build;
+import android.util.ArraySet;
import android.util.Log;
import android.view.Display;
+import android.view.WindowMetrics;
import androidx.annotation.AnyThread;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.ApiWrapper;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
/**
* Utility class to cache properties of default display to avoid a system RPC on every call.
@@ -54,13 +62,14 @@
public static final MainThreadInitializedObject<DisplayController> INSTANCE =
new MainThreadInitializedObject<>(DisplayController::new);
- public static final int CHANGE_SIZE = 1 << 0;
+ public static final int CHANGE_ACTIVE_SCREEN = 1 << 0;
public static final int CHANGE_ROTATION = 1 << 1;
public static final int CHANGE_FRAME_DELAY = 1 << 2;
public static final int CHANGE_DENSITY = 1 << 3;
+ public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 4;
- public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION
- | CHANGE_FRAME_DELAY | CHANGE_DENSITY;
+ public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
+ | CHANGE_FRAME_DELAY | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
private final Context mContext;
private final DisplayManager mDM;
@@ -87,7 +96,22 @@
new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
}
- mInfo = new Info(getContext(display), display);
+ // Create a single holder for all internal displays. External display holders created
+ // lazily.
+ Set<PortraitSize> extraInternalDisplays = new ArraySet<>();
+ for (Display d : mDM.getDisplays()) {
+ if (ApiWrapper.isInternalDisplay(display) && d.getDisplayId() != DEFAULT_DISPLAY) {
+ Point size = new Point();
+ d.getRealSize(size);
+ extraInternalDisplays.add(new PortraitSize(size.x, size.y));
+ }
+ }
+
+ if (extraInternalDisplays.isEmpty() || !Utilities.ATLEAST_S) {
+ mInfo = new Info(createDisplayInfoContext(display), display, Collections.emptySet());
+ } else {
+ mInfo = new Info(mWindowContext, display, extraInternalDisplays);
+ }
mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
}
@@ -139,7 +163,7 @@
*/
private void onConfigChanged(Intent intent) {
Configuration config = mContext.getResources().getConfiguration();
- if (config.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
+ if (mInfo.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
Log.d(TAG, "Configuration changed, notifying listeners");
Display display = mDM.getDisplay(DEFAULT_DISPLAY);
if (display != null) {
@@ -157,8 +181,7 @@
|| config.fontScale != mInfo.fontScale
|| display.getRotation() != mInfo.rotation
|| !mInfo.mScreenSizeDp.equals(
- Math.min(config.screenHeightDp, config.screenWidthDp),
- Math.max(config.screenHeightDp, config.screenWidthDp))) {
+ new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
handleInfoChange(display);
}
}
@@ -178,18 +201,23 @@
return mInfo;
}
- private Context getContext(Display display) {
- return Utilities.ATLEAST_S ? mWindowContext : mContext.createDisplayContext(display);
+ private Context createDisplayInfoContext(Display display) {
+ return Utilities.ATLEAST_S
+ ? mContext.createWindowContext(display, TYPE_APPLICATION, null)
+ : mContext.createDisplayContext(display);
}
@AnyThread
private void handleInfoChange(Display display) {
Info oldInfo = mInfo;
- Context context = getContext(display);
- Info newInfo = new Info(context, display);
+ Set<PortraitSize> extraDisplaysSizes = oldInfo.mAllSizes.size() > 1
+ ? oldInfo.mAllSizes : Collections.emptySet();
+
+ Context displayContext = createDisplayInfoContext(display);
+ Info newInfo = new Info(displayContext, display, extraDisplaysSizes);
int change = 0;
- if (newInfo.hasDifferentSize(oldInfo)) {
- change |= CHANGE_SIZE;
+ if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) {
+ change |= CHANGE_ACTIVE_SCREEN;
}
if (newInfo.rotation != oldInfo.rotation) {
change |= CHANGE_ROTATION;
@@ -200,11 +228,14 @@
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
change |= CHANGE_DENSITY;
}
+ if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
+ change |= CHANGE_SUPPORTED_BOUNDS;
+ }
if (change != 0) {
mInfo = newInfo;
final int flags = change;
- MAIN_EXECUTOR.execute(() -> notifyChange(context, flags));
+ MAIN_EXECUTOR.execute(() -> notifyChange(displayContext, flags));
}
}
@@ -224,13 +255,18 @@
public final float fontScale;
public final int densityDpi;
- private final Point mScreenSizeDp;
+ private final PortraitSize mScreenSizeDp;
+ private final Set<PortraitSize> mAllSizes;
- public final Point realSize;
- public final Point smallestSize;
- public final Point largestSize;
+ public final Point currentSize;
+
+ public final Set<WindowBounds> supportedBounds = new ArraySet<>();
public Info(Context context, Display display) {
+ this(context, display, Collections.emptySet());
+ }
+
+ private Info(Context context, Display display, Set<PortraitSize> extraDisplaysSizes) {
id = display.getDisplayId();
rotation = display.getRotation();
@@ -238,35 +274,67 @@
Configuration config = context.getResources().getConfiguration();
fontScale = config.fontScale;
densityDpi = config.densityDpi;
- mScreenSizeDp = new Point(
- Math.min(config.screenHeightDp, config.screenWidthDp),
- Math.max(config.screenHeightDp, config.screenWidthDp));
+ mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
singleFrameMs = getSingleFrameMs(display);
+ currentSize = new Point();
- realSize = new Point();
- smallestSize = new Point();
- largestSize = new Point();
+ display.getRealSize(currentSize);
- display.getRealSize(realSize);
- display.getCurrentSizeRange(smallestSize, largestSize);
+ if (extraDisplaysSizes.isEmpty() || !Utilities.ATLEAST_S) {
+ Point smallestSize = new Point();
+ Point largestSize = new Point();
+ display.getCurrentSizeRange(smallestSize, largestSize);
+
+ int portraitWidth = Math.min(currentSize.x, currentSize.y);
+ int portraitHeight = Math.max(currentSize.x, currentSize.y);
+
+ supportedBounds.add(new WindowBounds(portraitWidth, portraitHeight,
+ smallestSize.x, largestSize.y));
+ supportedBounds.add(new WindowBounds(portraitHeight, portraitWidth,
+ largestSize.x, smallestSize.y));
+ mAllSizes = Collections.singleton(new PortraitSize(currentSize.x, currentSize.y));
+ } else {
+ mAllSizes = new ArraySet<>(extraDisplaysSizes);
+ mAllSizes.add(new PortraitSize(currentSize.x, currentSize.y));
+ Set<WindowMetrics> metrics = WindowManagerCompat.getDisplayProfiles(
+ context, mAllSizes, densityDpi,
+ ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
+ metrics.forEach(wm -> supportedBounds.add(WindowBounds.fromWindowMetrics(wm)));
+ }
}
- private boolean hasDifferentSize(Info info) {
- if (!realSize.equals(info.realSize)
- && !realSize.equals(info.realSize.y, info.realSize.x)) {
- Log.d(TAG, String.format("Display size changed from %s to %s",
- info.realSize, realSize));
- return true;
- }
+ /**
+ * Returns true if the bounds represent a tablet
+ */
+ public boolean isTablet(WindowBounds bounds) {
+ return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
+ densityDpi) >= MIN_TABLET_WIDTH;
+ }
+ }
- if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
- Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
- smallestSize, largestSize, info.smallestSize, info.largestSize));
- return true;
- }
+ /**
+ * Utility class to hold a size information in an orientation independent way
+ */
+ public static class PortraitSize {
+ public final int width, height;
- return false;
+ public PortraitSize(int w, int h) {
+ width = Math.min(w, h);
+ height = Math.max(w, h);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PortraitSize that = (PortraitSize) o;
+ return width == that.width && height == that.height;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(width, height);
}
}
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index 3c2fb62..c92770e 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -15,11 +15,16 @@
*/
package com.android.launcher3.util;
+import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
+import android.view.WindowInsets.Type;
+import android.view.WindowMetrics;
import androidx.annotation.Nullable;
+import java.util.Objects;
+
/**
* Utility class to hold information about window position and layout
*/
@@ -36,6 +41,18 @@
bounds.height() - insets.top - insets.bottom);
}
+ public WindowBounds(int width, int height, int availableWidth, int availableHeight) {
+ this.bounds = new Rect(0, 0, width, height);
+ this.availableSize = new Point(availableWidth, availableHeight);
+ // We don't care about insets in this case
+ this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(bounds, insets);
+ }
+
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof WindowBounds)) {
@@ -44,4 +61,30 @@
WindowBounds other = (WindowBounds) obj;
return other.bounds.equals(bounds) && other.insets.equals(insets);
}
+
+ @Override
+ public String toString() {
+ return "WindowBounds{"
+ + "bounds=" + bounds
+ + ", insets=" + insets
+ + ", availableSize=" + availableSize
+ + '}';
+ }
+
+ /**
+ * Returns true if the device is in landscape orientation
+ */
+ public final boolean isLandscape() {
+ return availableSize.x > availableSize.y;
+ }
+
+ /**
+ * Returns the bounds corresponding to the provided WindowMetrics
+ */
+ @SuppressWarnings("NewApi")
+ public static WindowBounds fromWindowMetrics(WindowMetrics wm) {
+ Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+ return new WindowBounds(wm.getBounds(),
+ new Rect(insets.left, insets.top, insets.right, insets.bottom));
+ }
}
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
new file mode 100644
index 0000000..38a63de
--- /dev/null
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -0,0 +1,107 @@
+/*
+ * 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.util;
+
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.Utilities.dpiFromPx;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.util.DisplayController.PortraitSize;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Utility class to simulate window manager APIs until proper APIs are available
+ */
+@TargetApi(Build.VERSION_CODES.S)
+public class WindowManagerCompat {
+
+ public static final int MIN_TABLET_WIDTH = 600;
+
+ /**
+ * Returns a set of supported render sizes for a set of internal displays.
+ * This is a temporary workaround which assumes only nav-bar insets change across displays
+ * @param consumeTaskBar if true, it assumes that task bar is part of the app window
+ * and ignores any insets because of task bar.
+ */
+ public static Set<WindowMetrics> getDisplayProfiles(
+ Context windowContext, Collection<PortraitSize> allDisplaySizes,
+ int densityDpi, boolean consumeTaskBar) {
+ WindowInsets metrics = windowContext.getSystemService(WindowManager.class)
+ .getMaximumWindowMetrics().getWindowInsets();
+ boolean hasNavbar = ResourceUtils.getIntegerByName(
+ "config_navBarInteractionMode",
+ windowContext.getResources(),
+ INVALID_RESOURCE_HANDLE) != 0;
+
+ WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics);
+
+ Set<WindowMetrics> result = new HashSet<>();
+ for (PortraitSize size : allDisplaySizes) {
+ int swDP = (int) dpiFromPx(size.width, densityDpi);
+ boolean isTablet = swDP >= MIN_TABLET_WIDTH;
+
+ final Insets portraitNav, landscapeNav;
+ if (isTablet && !consumeTaskBar) {
+ portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
+ .getDimensionPixelSize(R.dimen.taskbar_size));
+ } else if (hasNavbar) {
+ portraitNav = Insets.of(0, 0, 0,
+ getSystemResource(windowContext, "navigation_bar_height", swDP));
+ landscapeNav = isTablet
+ ? Insets.of(0, 0, 0, getSystemResource(windowContext,
+ "navigation_bar_height_landscape", swDP))
+ : Insets.of(0, 0, getSystemResource(windowContext,
+ "navigation_bar_width", swDP), 0);
+ } else {
+ portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
+ }
+
+ result.add(new WindowMetrics(
+ new Rect(0, 0, size.width, size.height),
+ insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build()));
+ result.add(new WindowMetrics(
+ new Rect(0, 0, size.height, size.width),
+ insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build()));
+ }
+ return result;
+ }
+
+ private static int getSystemResource(Context context, String key, int swDp) {
+ int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
+ if (resourceId > 0) {
+ Configuration conf = new Configuration();
+ conf.smallestScreenWidthDp = swDp;
+ return context.createConfigurationContext(conf)
+ .getResources().getDimensionPixelSize(resourceId);
+ }
+ return 0;
+ }
+}
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
index 3a24c3d..149ac57 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -44,7 +44,7 @@
mPaint = new TextPaint();
mPaint.setColor(Color.WHITE);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
- mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx,
+ mLauncher.getDeviceProfile().iconTextSizePx,
getResources().getDisplayMetrics()));
setBackgroundResource(R.drawable.bg_deferred_app_widget);
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index ad61495..de511cd 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -13,6 +13,7 @@
import android.os.Parcel;
import android.os.UserHandle;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
@@ -66,20 +67,25 @@
}
public void initSpans(Context context, InvariantDeviceProfile idp) {
- Point landCellSize = idp.landscapeProfile.getCellSize();
- Point portCellSize = idp.portraitProfile.getCellSize();
-
// Always assume we're working with the smallest span to make sure we
// reserve enough space in both orientations.
- float smallestCellWidth = Math.min(landCellSize.x, portCellSize.x);
- float smallestCellHeight = Math.min(landCellSize.y, portCellSize.y);
+ float smallestCellWidth = Float.MAX_VALUE;
+ float smallestCellHeight = Float.MAX_VALUE;
+
+ Point cellSize = new Point();
+ boolean isWidgetPadded = false;
+ for (DeviceProfile dp : idp.supportedProfiles) {
+ dp.getCellSize(cellSize);
+ smallestCellWidth = Math.min(smallestCellWidth, cellSize.x);
+ smallestCellHeight = Math.min(smallestCellHeight, cellSize.y);
+ isWidgetPadded = isWidgetPadded || !dp.shouldInsetWidgets();
+ }
// We want to account for the extra amount of padding that we are adding to the widget
// to ensure that it gets the full amount of space that it has requested.
// If grids supports insetting widgets, we do not account for widget padding.
Rect widgetPadding = new Rect();
- if (!idp.landscapeProfile.shouldInsetWidgets()
- || !idp.portraitProfile.shouldInsetWidgets()) {
+ if (isWidgetPadded) {
AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index ee0b84e..3377abb 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -15,9 +15,11 @@
*/
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;
+import android.content.Context;
import android.os.Bundle;
import com.android.launcher3.LauncherSettings;
@@ -57,4 +59,8 @@
public WidgetAddFlowHandler getHandler() {
return new WidgetAddFlowHandler(info);
}
+
+ public Bundle getDefaultSizeOptions(Context context) {
+ return getWidgetSizeOptions(context, componentName, spanX, spanY);
+ }
}
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 12e0d43..46141e0 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -1,16 +1,12 @@
package com.android.launcher3.widget;
import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
import android.content.Context;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
-import android.util.SizeF;
import android.view.View;
-import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
import com.android.launcher3.dragndrop.DragController;
@@ -18,9 +14,6 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.util.Thunk;
-import java.util.ArrayList;
-import java.util.stream.Collectors;
-
public class WidgetHostViewLoader implements DragController.DragListener {
private static final String TAG = "WidgetHostViewLoader";
private static final boolean LOGD = false;
@@ -90,7 +83,7 @@
if (pInfo.isCustomWidget()) {
return false;
}
- final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
+ final Bundle options = mInfo.getDefaultSizeOptions(mLauncher);
// If there is a configuration activity, do not follow thru bound and inflate.
if (mInfo.getHandler().needsConfigure()) {
@@ -154,29 +147,4 @@
return true;
}
- public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
- ArrayList<SizeF> sizes = AppWidgetResizeFrame
- .getWidgetSizes(context, info.spanX, info.spanY);
-
- Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
- info.componentName, null);
- float density = context.getResources().getDisplayMetrics().density;
- float xPaddingDips = (padding.left + padding.right) / density;
- float yPaddingDips = (padding.top + padding.bottom) / density;
-
- ArrayList<SizeF> 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, null /* outRect */);
-
- 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;
- }
}