From 19ff728b921cecb77493f996d023cae6509601f5 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 22 Apr 2021 10:12:54 -0700 Subject: [PATCH] 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 --- .../launcher3/BaseQuickstepLauncher.java | 4 +- .../appprediction/PredictionRowView.java | 5 +- .../model/QuickstepModelDelegate.java | 6 +- .../launcher3/uioverrides/ApiWrapper.java | 10 + .../OrientationTouchTransformer.java | 6 +- .../RecentsAnimationDeviceState.java | 12 +- .../quickstep/RotationTouchHelper.java | 6 +- .../DeviceLockedInputConsumer.java | 2 +- .../OneHandedModeInputConsumer.java | 2 +- .../quickstep/util/RecentsOrientedState.java | 28 ++- .../quickstep/util/SplitScreenBounds.java | 7 +- res/values-sw600dp/config.xml | 3 - res/values/attrs.xml | 26 +- res/values/config.xml | 3 - .../launcher3/ui/LauncherUIScrollTest.java | 2 +- .../LauncherAppWidgetProviderInfoTest.java | 9 +- .../launcher3/AppWidgetResizeFrame.java | 121 ++++----- src/com/android/launcher3/DeviceProfile.java | 163 +++++------- src/com/android/launcher3/Hotseat.java | 6 +- .../launcher3/InvariantDeviceProfile.java | 236 ++++++++++-------- src/com/android/launcher3/Launcher.java | 4 +- src/com/android/launcher3/Workspace.java | 2 +- .../allapps/AllAppsContainerView.java | 2 +- .../launcher3/allapps/AllAppsGridAdapter.java | 2 +- .../launcher3/dragndrop/AddItemActivity.java | 3 +- .../graphics/GridCustomizationsProvider.java | 5 +- .../graphics/LauncherPreviewRenderer.java | 2 +- .../launcher3/qsb/QsbContainerView.java | 23 +- .../SecondaryDisplayLauncher.java | 4 +- .../secondarydisplay/SecondaryDragLayer.java | 7 +- .../launcher3/util/DisplayController.java | 146 ++++++++--- .../android/launcher3/util/WindowBounds.java | 43 ++++ .../launcher3/util/WindowManagerCompat.java | 107 ++++++++ .../widget/DeferredAppWidgetHostView.java | 2 +- .../widget/LauncherAppWidgetProviderInfo.java | 20 +- .../widget/PendingAddWidgetInfo.java | 6 + .../widget/WidgetHostViewLoader.java | 34 +-- .../launcher3/uioverrides/ApiWrapper.java | 10 + .../android/launcher3/common/WidgetUtils.java | 3 +- 39 files changed, 625 insertions(+), 457 deletions(-) create mode 100644 src/com/android/launcher3/util/WindowManagerCompat.java diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index c6c6c01542..cd22196686 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -19,7 +19,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.util.DisplayController.CHANGE_SIZE; +import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; @@ -240,7 +240,7 @@ public abstract class BaseQuickstepLauncher extends Launcher public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) { super.onDisplayInfoChanged(context, info, flags); - if ((flags & CHANGE_SIZE) != 0) { + if ((flags & CHANGE_ACTIVE_SCREEN) != 0) { addTaskbarIfNecessary(); } } diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index cc3ccea4d0..6d5975f5fa 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -33,7 +33,6 @@ import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; @@ -80,9 +79,9 @@ public class PredictionRowView extends LinearLayout implements setOrientation(LinearLayout.HORIZONTAL); mFocusHelper = new SimpleFocusIndicatorHelper(this); - mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns; mLauncher = Launcher.getLauncher(context); mLauncher.addOnDeviceProfileChangeListener(this); + mNumPredictedAppsPerRow = mLauncher.getDeviceProfile().numShownAllAppsColumns; updateVisibility(); } @@ -174,7 +173,7 @@ public class PredictionRowView extends LinearLayout implements @Override public void onDeviceProfileChanged(DeviceProfile dp) { - mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns; + mNumPredictedAppsPerRow = dp.numShownAllAppsColumns; removeAllViews(); applyPredictionApps(); } diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 8c6887250e..b0c13f99c0 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -104,8 +104,8 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange // TODO: Implement caching and preloading super.loadItems(ums, pinnedShortcuts); - WorkspaceItemFactory allAppsFactory = - new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns); + WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory( + mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns); mAllAppsState.items.setItems( mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items); @@ -204,7 +204,7 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange registerPredictor(mAllAppsState, apm.createAppPredictionSession( new AppPredictionContext.Builder(context) .setUiSurface("home") - .setPredictedTargetCount(mIDP.numAllAppsColumns) + .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns) .build())); // TODO: get bundle diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java index 1e03b05b5e..76a5782d11 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java @@ -18,13 +18,23 @@ package com.android.launcher3.uioverrides; import android.app.Person; import android.content.pm.ShortcutInfo; +import android.view.Display; import com.android.launcher3.Utilities; public class ApiWrapper { + public static final boolean TASKBAR_DRAWN_IN_PROCESS = true; + public static Person[] getPersons(ShortcutInfo si) { Person[] persons = si.getPersons(); return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons; } + + /** + * Returns true if the display is an internal displays + */ + public static boolean isInternalDisplay(Display display) { + return display.getType() == Display.TYPE_INTERNAL; + } } diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index c47300cd5f..62b821c41d 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -191,7 +191,7 @@ class OrientationTouchTransformer { * @see #enableMultipleRegions(boolean, Info) */ void createOrAddTouchRegion(Info info) { - mCurrentDisplay = new CurrentDisplay(info.realSize, info.rotation); + mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation); if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED && mCurrentDisplay.rotation == mQuickStepStartingRotation) { @@ -256,7 +256,7 @@ class OrientationTouchTransformer { Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation); } - mCurrentDisplay = new CurrentDisplay(region.realSize, region.rotation); + mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation); OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay); if (DEBUG) { Log.d(TestProtocol.NO_SWIPE_TO_HOME, "cached region: " + regionToKeep @@ -289,7 +289,7 @@ class OrientationTouchTransformer { + " with mode: " + mMode + " displayRotation: " + display.rotation); } - Point size = display.realSize; + Point size = display.currentSize; int rotation = display.rotation; int touchHeight = mNavBarGesturalHeight; OrientationRectF orientationRectF = diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index ef09957c9d..e271203d18 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -18,7 +18,7 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; -import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY; +import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; @@ -271,15 +271,9 @@ public class RecentsAnimationDeviceState implements @Override public void onDisplayInfoChanged(Context context, Info info, int flags) { - if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) { - // ignore displays that aren't running launcher and frame refresh rate changes - return; + if ((flags & CHANGE_ROTATION) != 0) { + mNavBarPosition = new NavBarPosition(mMode, info); } - - if (!mMode.hasGestures) { - return; - } - mNavBarPosition = new NavBarPosition(mMode, info); } @Override diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java index fd0de4212f..76298624d3 100644 --- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java +++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java @@ -17,8 +17,9 @@ package com.android.quickstep; import static android.view.Surface.ROTATION_0; +import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN; import static com.android.launcher3.util.DisplayController.CHANGE_ALL; -import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY; +import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; @@ -274,8 +275,7 @@ public class RotationTouchHelper implements @Override public void onDisplayInfoChanged(Context context, Info info, int flags) { - if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) { - // ignore displays that aren't running launcher and frame refresh rate changes + if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) { return; } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 9c647946ea..fcc0217499 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -116,7 +116,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, R.dimen.device_locked_y_offset); // Do not use DeviceProfile as the user data might be locked - mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().realSize; + mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize; // Init states mStateCallback = new MultiStateCallback(STATE_NAMES); diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java index d82d43d7f8..8d9c524a57 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java @@ -70,7 +70,7 @@ public class OneHandedModeInputConsumer extends DelegateInputConsumer { mDragDistThreshold = context.getResources().getDimensionPixelSize( R.dimen.gestures_onehanded_drag_threshold); mSquaredSlop = Utilities.squaredTouchSlop(context); - mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().realSize; + mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize; mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources()); } diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index 188efadd45..d164c8ce3e 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -33,6 +33,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Matrix; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.util.Log; @@ -49,6 +50,7 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.WindowBounds; import com.android.quickstep.BaseActivityInterface; @@ -561,11 +563,27 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre */ public DeviceProfile getLauncherDeviceProfile() { InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext); - // TODO also check the natural orientation is landscape or portrait - return (mRecentsActivityRotation == ROTATION_90 - || mRecentsActivityRotation == ROTATION_270) - ? idp.landscapeProfile - : idp.portraitProfile; + Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize; + + int width, height; + if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) { + width = Math.max(currentSize.x, currentSize.y); + height = Math.min(currentSize.x, currentSize.y); + } else { + width = Math.min(currentSize.x, currentSize.y); + height = Math.max(currentSize.x, currentSize.y); + } + + DeviceProfile bestMatch = idp.supportedProfiles.get(0); + float minDiff = Float.MAX_VALUE; + for (DeviceProfile profile : idp.supportedProfiles) { + float diff = Math.abs(profile.widthPx - width) + Math.abs(profile.heightPx - height); + if (diff < minDiff) { + minDiff = diff; + bestMatch = profile; + } + } + return bestMatch; } private static String nameAndAddress(Object obj) { diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java index 88cc650276..483a1c652a 100644 --- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java +++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java @@ -20,10 +20,7 @@ import static android.view.Surface.ROTATION_180; import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Insets; -import android.graphics.Rect; import android.os.Build; -import android.view.WindowInsets.Type; import android.view.WindowManager; import android.view.WindowMetrics; @@ -73,10 +70,8 @@ public class SplitScreenBounds { */ private static WindowBounds createDefaultWindowBounds(Context context) { WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); - Insets insets = wm.getWindowInsets().getInsets(Type.systemBars()); + WindowBounds bounds = WindowBounds.fromWindowMetrics(wm); - WindowBounds bounds = new WindowBounds(wm.getBounds(), - new Rect(insets.left, insets.top, insets.right, insets.bottom)); int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation; int halfDividerSize = context.getResources() .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2; diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml index d50115b20d..09bdaafc1d 100644 --- a/res/values-sw600dp/config.xml +++ b/res/values-sw600dp/config.xml @@ -1,6 +1,3 @@ true - - - false diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 93d88c2e94..a81802d87a 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -127,17 +127,24 @@ + + + + + + + + - - - @@ -167,8 +174,12 @@ - - + + + + + + @@ -176,11 +187,6 @@ - - - - - diff --git a/res/values/config.xml b/res/values/config.xml index 1e7837353f..9ad12245be 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -55,9 +55,6 @@ 200 - - true - diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java index 4d151f1948..ea755484c1 100644 --- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java +++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java @@ -161,7 +161,7 @@ public class LauncherUIScrollTest { private static MotionEvent createScrollEvent(int scroll) { DeviceProfile dp = InvariantDeviceProfile.INSTANCE - .get(RuntimeEnvironment.application).portraitProfile; + .get(RuntimeEnvironment.application).supportedProfiles.get(0); final PointerProperties[] pointerProperties = new PointerProperties[1]; pointerProperties[0] = new PointerProperties(); diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java index 92f77f2bbd..d97701148b 100644 --- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java +++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java @@ -17,6 +17,9 @@ package com.android.launcher3.widget; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; + import android.content.Context; import android.graphics.Point; @@ -109,10 +112,14 @@ public final class LauncherAppWidgetProviderInfoTest { private InvariantDeviceProfile createIDP() { DeviceProfile profile = Mockito.mock(DeviceProfile.class); + doAnswer(i -> { + ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE); + return null; + }).when(profile).getCellSize(any(Point.class)); Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE)); InvariantDeviceProfile idp = new InvariantDeviceProfile(); - idp.landscapeProfile = idp.portraitProfile = profile; + idp.supportedProfiles.add(profile); return idp; } diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 5d41bb5c3e..20b4631dec 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.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; +import android.content.ComponentName; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; @@ -25,16 +29,14 @@ import android.view.View; 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 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O private static final Rect sTmpRect = new Rect(); - // Represents the cell size on the grid in the two orientations. - public static final MainThreadInitializedObject 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 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 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O 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 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O mWidgetView.requestLayout(); } - public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher, - int spanX, int spanY) { - List sizes = getWidgetSizes(launcher, spanX, spanY); + public static void updateWidgetSizeRanges( + AppWidgetHostView widgetView, Context context, int spanX, int spanY) { + List sizes = getWidgetSizes(context, spanX, spanY); if (ATLEAST_S) { widgetView.updateAppWidgetSize(new Bundle(), sizes); } else { - Rect bounds = getMinMaxSizes(sizes, 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 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 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 sizes = getWidgetSizes(context, spanX, spanY); + Rect padding = getDefaultPaddingForWidget(context, provider, null); + float density = context.getResources().getDisplayMetrics().density; + float xPaddingDips = (padding.left + padding.right) / density; + float yPaddingDips = (padding.top + padding.bottom) / density; + + ArrayList paddedSizes = sizes.stream() + .map(size -> new SizeF( + Math.max(0.f, size.getWidth() - xPaddingDips), + Math.max(0.f, size.getHeight() - yPaddingDips))) + .collect(Collectors.toCollection(ArrayList::new)); + + Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes); + Bundle options = new Bundle(); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right); + options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom); + options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes); + return options; + } + /** * Returns the min and max widths and heights given a list of sizes, in dp. * * @param sizes List of sizes to get the min/max from. - * @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 sizes, @Nullable Rect outRect) { - if (outRect == null) { - outRect = new Rect(); - } + private static Rect getMinMaxSizes(List 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 fd9793625e..d5860dc57d 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -31,7 +31,6 @@ import android.graphics.PointF; 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.config.FeatureFlags; 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 @@ import java.io.PrintWriter; @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 @@ public class DeviceProfile { // 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 class DeviceProfile { public int allAppsCellWidthPx; public int allAppsIconSizePx; public int allAppsIconDrawablePaddingPx; + public final int numShownAllAppsColumns; public float allAppsIconTextSizePx; // Overview @@ -194,42 +190,30 @@ public class DeviceProfile { // 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 @@ public class DeviceProfile { ? 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 @@ public class DeviceProfile { 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 class DeviceProfile { } 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 @@ public class DeviceProfile { * 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(); @@ -433,14 +414,6 @@ public class DeviceProfile { return profile; } - /** - * 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. @@ -553,7 +526,7 @@ public class DeviceProfile { } // 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 class DeviceProfile { } 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 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { // 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 @@ -801,13 +776,6 @@ public class DeviceProfile { return isLandscape && transposeLayoutWithOrientation; } - /** - * 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. */ @@ -828,7 +796,7 @@ public class DeviceProfile { } public boolean shouldFadeAdjacentWorkspaceScreens() { - return isVerticalBarLayout() || isLargeTablet; + return isVerticalBarLayout(); } public int getCellHeight(@ContainerType int containerType) { @@ -854,13 +822,13 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { hotseatBarSidePaddingStartPx)); writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx", hotseatBarSidePaddingEndPx)); + writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons); writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent); @@ -967,41 +937,16 @@ public class DeviceProfile { 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 @@ public class DeviceProfile { 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 class DeviceProfile { } 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 8496fd5d9c..b2a9e75e50 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -90,11 +90,11 @@ public class Hotseat extends CellLayout implements Insettable { 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 1332e14af0..b263d3830e 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.DisplayController.Info; 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 org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.List; public class InvariantDeviceProfile { @@ -73,6 +76,9 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { * 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 class InvariantDeviceProfile { public int defaultLayoutId; int demoModeLayoutId; - public DeviceProfile landscapeProfile; - public DeviceProfile portraitProfile; + public final List supportedProfiles = new ArrayList<>(); @Nullable public DevicePaddings devicePaddings; @@ -175,6 +181,7 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { // 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 @@ public class InvariantDeviceProfile { private String initGrid(Context context, String gridName) { Info displayInfo = DisplayController.INSTANCE.get(context).getInfo(); - ArrayList 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 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { // 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 @@ public class InvariantDeviceProfile { } 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 @@ public class InvariantDeviceProfile { 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 getPredefinedDeviceProfiles(Context context, String gridName) { + private static ArrayList getPredefinedDeviceProfiles( + Context context, String gridName, boolean isSplitDisplay) { ArrayList profiles = new ArrayList<>(); try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { final int depth = parser.getDepth(); @@ -449,8 +465,9 @@ public class InvariantDeviceProfile { && 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 @@ public class InvariantDeviceProfile { return (float) Math.hypot(x1 - x0, y1 - y0); } - @VisibleForTesting - static DisplayOption invDistWeightedInterpolate( - Info displayInfo, ArrayList points) { - Point smallestSize = new Point(displayInfo.smallestSize); - Point largestSize = new Point(displayInfo.largestSize); + private static DisplayOption invDistWeightedInterpolate( + Info displayInfo, ArrayList 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 @@ public class InvariantDeviceProfile { return out.multiply(1.0f / weights); } - @VisibleForTesting - static DisplayOption invDistWeightedInterpolate(float width, float height, - ArrayList 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { private final boolean isScalable; private final int devicePaddingId; - public final boolean visible; - private final SparseArray extraAttrs; public GridOption(Context context, AttributeSet attrs) { @@ -665,8 +692,17 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { 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 @@ public class InvariantDeviceProfile { } 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 f640118552..09c7b7aff0 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -189,7 +189,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo; 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 @@ public class Launcher extends StatefulActivity implements Launche 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 05d6e045a6..7c5f99e07b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -456,7 +456,7 @@ public class Workspace extends PagedView } 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 40f7ab167c..119a91ffba 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -157,7 +157,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo @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 5b4c4c5c36..70588ea317 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -283,7 +283,7 @@ public class AllAppsGridAdapter extends 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 d5a04a647d..5ba36f21e3 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.PendingAddShortcutInfo; 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 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener 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 911f8c3cde..cb42e7aa05 100644 --- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java +++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java @@ -90,10 +90,7 @@ public class GridCustomizationsProvider extends ContentProvider { 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 31764c57f5..6a8ac64571 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -224,7 +224,7 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper 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 a191df4b38..22c3f58574 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.ACTION_APPWIDGET_BIND; 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.appwidget.AppWidgetProviderInfo; 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 android.widget.FrameLayout; 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.Utilities; 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 @@ public class QsbContainerView extends FrameLayout { 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 @@ public class QsbContainerView extends FrameLayout { protected Bundle createBindOptions() { InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext()); - - Bundle opts = new Bundle(); - ArrayList 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 f5e74b7168..5999091786 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -87,8 +87,8 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity 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 40630d3c2e..f78f6dd40c 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java @@ -29,7 +29,6 @@ import android.widget.GridView; 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 @@ public class SecondaryDragLayer extends BaseDragLayer 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 @@ public class SecondaryDragLayer extends BaseDragLayer 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 75c089e754..b7512077a9 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -18,8 +18,10 @@ package com.android.launcher3.util; 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.graphics.Point; 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 class DisplayController implements DisplayListener, ComponentCallbacks { public static final MainThreadInitializedObject 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 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { 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 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 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { */ 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 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { || 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 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { 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 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 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { 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 class DisplayController implements DisplayListener, ComponentCallbacks { public final float fontScale; public final int densityDpi; - private final Point mScreenSizeDp; + private final PortraitSize mScreenSizeDp; + private final Set mAllSizes; - public final Point realSize; - public final Point smallestSize; - public final Point largestSize; + public final Point currentSize; + + public final Set supportedBounds = new ArraySet<>(); public Info(Context context, Display display) { + this(context, display, Collections.emptySet()); + } + + private Info(Context context, Display display, Set extraDisplaysSizes) { id = display.getDisplayId(); rotation = display.getRotation(); @@ -238,35 +274,67 @@ public class DisplayController implements DisplayListener, ComponentCallbacks { 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 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 3c2fb62c16..c92770e8fd 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 @@ public class WindowBounds { 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 @@ public class WindowBounds { 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 0000000000..38a63de592 --- /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 getDisplayProfiles( + Context windowContext, Collection 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 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 3a24c3d21a..149ac57bfb 100644 --- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java @@ -44,7 +44,7 @@ public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView { 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 ad61495de8..de511cd00b 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java @@ -13,6 +13,7 @@ import android.graphics.drawable.Drawable; 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 class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo } 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 ee0b84e815..3377abbb7b 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 class PendingAddWidgetInfo extends PendingAddItemInfo { 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 12e0d43bd1..46141e0bc3 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.DragLayer; 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 @@ public class WidgetHostViewLoader implements DragController.DragListener { 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 @@ public class WidgetHostViewLoader implements DragController.DragListener { return true; } - public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) { - ArrayList 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 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; - } } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java index 6fd147a055..4407fe1149 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java @@ -18,12 +18,22 @@ package com.android.launcher3.uioverrides; import android.app.Person; import android.content.pm.ShortcutInfo; +import android.view.Display; import com.android.launcher3.Utilities; public class ApiWrapper { + public static final boolean TASKBAR_DRAWN_IN_PROCESS = false; + public static Person[] getPersons(ShortcutInfo si) { return Utilities.EMPTY_PERSON_ARRAY; } + + /** + * Returns true if the display is an internal displays + */ + public static boolean isInternalDisplay(Display display) { + return display.getDisplayId() == Display.DEFAULT_DISPLAY; + } } diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src_common/com/android/launcher3/common/WidgetUtils.java index ffad93fffc..5e17e0a5a7 100644 --- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java +++ b/tests/src_common/com/android/launcher3/common/WidgetUtils.java @@ -16,7 +16,6 @@ package com.android.launcher3.common; import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; -import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget; import android.appwidget.AppWidgetHost; import android.content.ContentResolver; @@ -62,7 +61,7 @@ public class WidgetUtils { pendingInfo.spanY = item.spanY; pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanY = item.minSpanY; - Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo); + Bundle options = pendingInfo.getDefaultSizeOptions(targetContext); AppWidgetHost host = new LauncherAppWidgetHost(targetContext); int widgetId = host.allocateAppWidgetId();