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
This commit is contained in:
Sunny Goyal 2021-04-22 10:12:54 -07:00
parent 0a1b030bb3
commit 19ff728b92
39 changed files with 625 additions and 457 deletions

View File

@ -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.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.FLAG_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.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.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; 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, public void onDisplayInfoChanged(Context context, DisplayController.Info info,
int flags) { int flags) {
super.onDisplayInfoChanged(context, info, flags); super.onDisplayInfoChanged(context, info, flags);
if ((flags & CHANGE_SIZE) != 0) { if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
addTaskbarIfNecessary(); addTaskbarIfNecessary();
} }
} }

View File

@ -33,7 +33,6 @@ import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView; import com.android.launcher3.allapps.FloatingHeaderView;
@ -80,9 +79,9 @@ public class PredictionRowView extends LinearLayout implements
setOrientation(LinearLayout.HORIZONTAL); setOrientation(LinearLayout.HORIZONTAL);
mFocusHelper = new SimpleFocusIndicatorHelper(this); mFocusHelper = new SimpleFocusIndicatorHelper(this);
mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns;
mLauncher = Launcher.getLauncher(context); mLauncher = Launcher.getLauncher(context);
mLauncher.addOnDeviceProfileChangeListener(this); mLauncher.addOnDeviceProfileChangeListener(this);
mNumPredictedAppsPerRow = mLauncher.getDeviceProfile().numShownAllAppsColumns;
updateVisibility(); updateVisibility();
} }
@ -174,7 +173,7 @@ public class PredictionRowView extends LinearLayout implements
@Override @Override
public void onDeviceProfileChanged(DeviceProfile dp) { public void onDeviceProfileChanged(DeviceProfile dp) {
mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns; mNumPredictedAppsPerRow = dp.numShownAllAppsColumns;
removeAllViews(); removeAllViews();
applyPredictionApps(); applyPredictionApps();
} }

View File

@ -104,8 +104,8 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange
// TODO: Implement caching and preloading // TODO: Implement caching and preloading
super.loadItems(ums, pinnedShortcuts); super.loadItems(ums, pinnedShortcuts);
WorkspaceItemFactory allAppsFactory = WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns); mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
mAllAppsState.items.setItems( mAllAppsState.items.setItems(
mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items); mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
@ -204,7 +204,7 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange
registerPredictor(mAllAppsState, apm.createAppPredictionSession( registerPredictor(mAllAppsState, apm.createAppPredictionSession(
new AppPredictionContext.Builder(context) new AppPredictionContext.Builder(context)
.setUiSurface("home") .setUiSurface("home")
.setPredictedTargetCount(mIDP.numAllAppsColumns) .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns)
.build())); .build()));
// TODO: get bundle // TODO: get bundle

View File

@ -18,13 +18,23 @@ package com.android.launcher3.uioverrides;
import android.app.Person; import android.app.Person;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.view.Display;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
public class ApiWrapper { public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
public static Person[] getPersons(ShortcutInfo si) { public static Person[] getPersons(ShortcutInfo si) {
Person[] persons = si.getPersons(); Person[] persons = si.getPersons();
return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons; 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;
}
} }

View File

@ -191,7 +191,7 @@ class OrientationTouchTransformer {
* @see #enableMultipleRegions(boolean, Info) * @see #enableMultipleRegions(boolean, Info)
*/ */
void createOrAddTouchRegion(Info info) { void createOrAddTouchRegion(Info info) {
mCurrentDisplay = new CurrentDisplay(info.realSize, info.rotation); mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation);
if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
&& mCurrentDisplay.rotation == mQuickStepStartingRotation) { && mCurrentDisplay.rotation == mQuickStepStartingRotation) {
@ -256,7 +256,7 @@ class OrientationTouchTransformer {
Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation); 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); OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
if (DEBUG) { if (DEBUG) {
Log.d(TestProtocol.NO_SWIPE_TO_HOME, "cached region: " + regionToKeep Log.d(TestProtocol.NO_SWIPE_TO_HOME, "cached region: " + regionToKeep
@ -289,7 +289,7 @@ class OrientationTouchTransformer {
+ " with mode: " + mMode + " displayRotation: " + display.rotation); + " with mode: " + mMode + " displayRotation: " + display.rotation);
} }
Point size = display.realSize; Point size = display.currentSize;
int rotation = display.rotation; int rotation = display.rotation;
int touchHeight = mNavBarGesturalHeight; int touchHeight = mNavBarGesturalHeight;
OrientationRectF orientationRectF = OrientationRectF orientationRectF =

View File

@ -18,7 +18,7 @@ package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED; 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_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_ENABLED;
import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED; import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
@ -271,15 +271,9 @@ public class RecentsAnimationDeviceState implements
@Override @Override
public void onDisplayInfoChanged(Context context, Info info, int flags) { public void onDisplayInfoChanged(Context context, Info info, int flags) {
if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) { if ((flags & CHANGE_ROTATION) != 0) {
// ignore displays that aren't running launcher and frame refresh rate changes mNavBarPosition = new NavBarPosition(mMode, info);
return;
} }
if (!mMode.hasGestures) {
return;
}
mNavBarPosition = new NavBarPosition(mMode, info);
} }
@Override @Override

View File

@ -17,8 +17,9 @@ package com.android.quickstep;
import static android.view.Surface.ROTATION_0; 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_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.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
@ -274,8 +275,7 @@ public class RotationTouchHelper implements
@Override @Override
public void onDisplayInfoChanged(Context context, Info info, int flags) { public void onDisplayInfoChanged(Context context, Info info, int flags) {
if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) { if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) {
// ignore displays that aren't running launcher and frame refresh rate changes
return; return;
} }

View File

@ -116,7 +116,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
R.dimen.device_locked_y_offset); R.dimen.device_locked_y_offset);
// Do not use DeviceProfile as the user data might be locked // 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 // Init states
mStateCallback = new MultiStateCallback(STATE_NAMES); mStateCallback = new MultiStateCallback(STATE_NAMES);

View File

@ -70,7 +70,7 @@ public class OneHandedModeInputConsumer extends DelegateInputConsumer {
mDragDistThreshold = context.getResources().getDimensionPixelSize( mDragDistThreshold = context.getResources().getDimensionPixelSize(
R.dimen.gestures_onehanded_drag_threshold); R.dimen.gestures_onehanded_drag_threshold);
mSquaredSlop = Utilities.squaredTouchSlop(context); 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, mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE,
mContext.getResources()); mContext.getResources());
} }

View File

@ -33,6 +33,7 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Matrix; import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.util.Log; import android.util.Log;
@ -49,6 +50,7 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SettingsCache; import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface; import com.android.quickstep.BaseActivityInterface;
@ -561,11 +563,27 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
*/ */
public DeviceProfile getLauncherDeviceProfile() { public DeviceProfile getLauncherDeviceProfile() {
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext); InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
// TODO also check the natural orientation is landscape or portrait Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
return (mRecentsActivityRotation == ROTATION_90
|| mRecentsActivityRotation == ROTATION_270) int width, height;
? idp.landscapeProfile if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) {
: idp.portraitProfile; 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) { private static String nameAndAddress(Object obj) {

View File

@ -20,10 +20,7 @@ import static android.view.Surface.ROTATION_180;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Context; import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.view.WindowInsets.Type;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.WindowMetrics; import android.view.WindowMetrics;
@ -73,10 +70,8 @@ public class SplitScreenBounds {
*/ */
private static WindowBounds createDefaultWindowBounds(Context context) { private static WindowBounds createDefaultWindowBounds(Context context) {
WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); 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 rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
int halfDividerSize = context.getResources() int halfDividerSize = context.getResources()
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2; .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;

View File

@ -1,6 +1,3 @@
<resources> <resources>
<bool name="allow_rotation">true</bool> <bool name="allow_rotation">true</bool>
<!-- Hotseat -->
<bool name="hotseat_transpose_layout_with_orientation">false</bool>
</resources> </resources>

View File

@ -127,17 +127,24 @@
<!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified --> <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
<attr name="numFolderRows" format="integer" /> <attr name="numFolderRows" format="integer" />
<attr name="numFolderColumns" format="integer" /> <attr name="numFolderColumns" format="integer" />
<!-- numAllAppsColumns defaults to numColumns, if not specified -->
<attr name="numAllAppsColumns" format="integer" />
<!-- Number of columns to use when extending the all-apps size,
defaults to 2 * numAllAppsColumns -->
<attr name="numExtendedAllAppsColumns" format="integer" />
<!-- numHotseatIcons defaults to numColumns, if not specified --> <!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" /> <attr name="numHotseatIcons" format="integer" />
<!-- Number of icons to use when extending the hotseat size,
defaults to 2 * numHotseatIcons -->
<attr name="numExtendedHotseatIcons" format="integer" />
<attr name="dbFile" format="string" /> <attr name="dbFile" format="string" />
<attr name="defaultLayoutId" format="reference" /> <attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" /> <attr name="demoModeLayoutId" format="reference" />
<attr name="isScalable" format="boolean" /> <attr name="isScalable" format="boolean" />
<attr name="devicePaddingId" format="reference" /> <attr name="devicePaddingId" format="reference" />
<!-- whether the grid option is shown to the user -->
<attr name="visible" format="boolean" />
</declare-styleable> </declare-styleable>
<declare-styleable name="DevicePadding"> <declare-styleable name="DevicePadding">
@ -167,8 +174,12 @@
<attr name="iconTextSize" format="float" /> <attr name="iconTextSize" format="float" />
<!-- landscapeIconTextSize defaults to iconTextSize, if not specified --> <!-- landscapeIconTextSize defaults to iconTextSize, if not specified -->
<attr name="landscapeIconTextSize" format="float" /> <attr name="landscapeIconTextSize" format="float" />
<!-- If true, this display option is used to determine the default grid -->
<attr name="canBeDefault" format="boolean" /> <!-- If set, this display option is used to determine the default grid -->
<attr name="canBeDefault" format="boolean|integer" >
<!-- The profile can be default on split display devices -->
<flag name="split_display" value="0x2" />
</attr>
<!-- The following values are only enabled if grid is supported. --> <!-- The following values are only enabled if grid is supported. -->
<!-- allAppsIconSize defaults to iconSize, if not specified --> <!-- allAppsIconSize defaults to iconSize, if not specified -->
@ -176,11 +187,6 @@
<!-- allAppsIconTextSize defaults to iconTextSize, if not specified --> <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
<attr name="allAppsIconTextSize" format="float" /> <attr name="allAppsIconTextSize" format="float" />
<!-- numAllAppsColumns defaults to GridDisplayOption.numColumns, if not specified -->
<attr name="numAllAppsColumns" format="integer" />
<!-- numShownHotseatIcons defaults to GridDisplayOption.numHotseatIcons, if not specified -->
<attr name="numShownHotseatIcons" format="integer" />
</declare-styleable> </declare-styleable>
<declare-styleable name="CellLayout"> <declare-styleable name="CellLayout">

View File

@ -55,9 +55,6 @@
<!-- The duration of the caret animation --> <!-- The duration of the caret animation -->
<integer name="config_caretAnimationDuration">200</integer> <integer name="config_caretAnimationDuration">200</integer>
<!-- Hotseat -->
<bool name="hotseat_transpose_layout_with_orientation">true</bool>
<!-- Various classes overriden by projects/build flavors. --> <!-- Various classes overriden by projects/build flavors. -->
<string name="folder_name_provider_class" translatable="false"></string> <string name="folder_name_provider_class" translatable="false"></string>
<string name="stats_log_manager_class" translatable="false"></string> <string name="stats_log_manager_class" translatable="false"></string>

View File

@ -161,7 +161,7 @@ public class LauncherUIScrollTest {
private static MotionEvent createScrollEvent(int scroll) { private static MotionEvent createScrollEvent(int scroll) {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE DeviceProfile dp = InvariantDeviceProfile.INSTANCE
.get(RuntimeEnvironment.application).portraitProfile; .get(RuntimeEnvironment.application).supportedProfiles.get(0);
final PointerProperties[] pointerProperties = new PointerProperties[1]; final PointerProperties[] pointerProperties = new PointerProperties[1];
pointerProperties[0] = new PointerProperties(); pointerProperties[0] = new PointerProperties();

View File

@ -17,6 +17,9 @@ package com.android.launcher3.widget;
import static com.google.common.truth.Truth.assertThat; 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.content.Context;
import android.graphics.Point; import android.graphics.Point;
@ -109,10 +112,14 @@ public final class LauncherAppWidgetProviderInfoTest {
private InvariantDeviceProfile createIDP() { private InvariantDeviceProfile createIDP() {
DeviceProfile profile = Mockito.mock(DeviceProfile.class); 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)); Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
InvariantDeviceProfile idp = new InvariantDeviceProfile(); InvariantDeviceProfile idp = new InvariantDeviceProfile();
idp.landscapeProfile = idp.portraitProfile = profile; idp.supportedProfiles.add(profile);
return idp; return idp;
} }

View File

@ -1,5 +1,7 @@
package com.android.launcher3; 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_HEIGHT;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH; import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
import static com.android.launcher3.Utilities.ATLEAST_S; import static com.android.launcher3.Utilities.ATLEAST_S;
@ -10,7 +12,9 @@ import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder; import android.animation.PropertyValuesHolder;
import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo; import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
@ -25,16 +29,14 @@ import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.Nullable;
import com.android.launcher3.accessibility.DragViewStateAnnouncer; import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener { public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
private static final int SNAP_DURATION = 150; 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(); private static final Rect sTmpRect = new Rect();
// Represents the cell size on the grid in the two orientations.
public static final MainThreadInitializedObject<Point[]> CELL_SIZE =
new MainThreadInitializedObject<>(c -> {
InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
return new Point[] {inv.landscapeProfile.getCellSize(),
inv.portraitProfile.getCellSize()};
});
// Represents the border spacing size on the grid in the two orientations.
public static final MainThreadInitializedObject<int[]> BORDER_SPACING_SIZE =
new MainThreadInitializedObject<>(c -> {
InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
return new int[] {inv.landscapeProfile.cellLayoutBorderSpacingPx,
inv.portraitProfile.cellLayoutBorderSpacingPx};
});
private static final int HANDLE_COUNT = 4; private static final int HANDLE_COUNT = 4;
private static final int INDEX_LEFT = 0; private static final int INDEX_LEFT = 0;
private static final int INDEX_TOP = 1; private static final int INDEX_TOP = 1;
@ -202,7 +188,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
mMaxHSpan = info.maxSpanX; mMaxHSpan = info.maxSpanX;
mMaxVSpan = info.maxSpanY; mMaxVSpan = info.maxSpanY;
mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(), mWidgetPadding = getDefaultPaddingForWidget(getContext(),
widgetView.getAppWidgetInfo().provider, null); widgetView.getAppWidgetInfo().provider, null);
if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) { if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
@ -392,81 +378,82 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
mWidgetView.requestLayout(); mWidgetView.requestLayout();
} }
public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher, public static void updateWidgetSizeRanges(
int spanX, int spanY) { AppWidgetHostView widgetView, Context context, int spanX, int spanY) {
List<SizeF> sizes = getWidgetSizes(launcher, spanX, spanY); List<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
if (ATLEAST_S) { if (ATLEAST_S) {
widgetView.updateAppWidgetSize(new Bundle(), sizes); widgetView.updateAppWidgetSize(new Bundle(), sizes);
} else { } else {
Rect bounds = getMinMaxSizes(sizes, null /* outRect */); Rect bounds = getMinMaxSizes(sizes);
widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right, widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
bounds.bottom); 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. */ /** Returns the list of sizes for a widget of given span, in dp. */
public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) { public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) {
final Point[] cellSize = CELL_SIZE.get(context);
final int[] borderSpacing = BORDER_SPACING_SIZE.get(context);
SizeF landSize = getWidgetSize(context, cellSize[0], spanX, spanY, borderSpacing[0]);
SizeF portSize = getWidgetSize(context, cellSize[1], spanX, spanY, borderSpacing[1]);
ArrayList<SizeF> sizes = new ArrayList<>(2); ArrayList<SizeF> sizes = new ArrayList<>(2);
sizes.add(landSize); final float density = context.getResources().getDisplayMetrics().density;
sizes.add(portSize); 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; return sizes;
} }
/**
* Returns the bundle to be used as the default options for a widget with provided size
*/
public static Bundle getWidgetSizeOptions(
Context context, ComponentName provider, int spanX, int spanY) {
ArrayList<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
Rect padding = getDefaultPaddingForWidget(context, provider, null);
float density = context.getResources().getDisplayMetrics().density;
float xPaddingDips = (padding.left + padding.right) / density;
float yPaddingDips = (padding.top + padding.bottom) / density;
ArrayList<SizeF> paddedSizes = sizes.stream()
.map(size -> new SizeF(
Math.max(0.f, size.getWidth() - xPaddingDips),
Math.max(0.f, size.getHeight() - yPaddingDips)))
.collect(Collectors.toCollection(ArrayList::new));
Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes);
Bundle options = new Bundle();
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
return options;
}
/** /**
* Returns the min and max widths and heights given a list of sizes, in dp. * 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 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 * @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 * the right (resp. bottom) for the max. The returned rectangle is set with 0s if the list is
* empty. * empty.
*/ */
public static Rect getMinMaxSizes(List<SizeF> sizes, @Nullable Rect outRect) { private static Rect getMinMaxSizes(List<SizeF> sizes) {
if (outRect == null) {
outRect = new Rect();
}
if (sizes.isEmpty()) { if (sizes.isEmpty()) {
outRect.set(0, 0, 0, 0); return new Rect();
} else { } else {
SizeF first = sizes.get(0); SizeF first = sizes.get(0);
outRect.set((int) first.getWidth(), (int) first.getHeight(), (int) first.getWidth(), Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(),
(int) first.getHeight()); (int) first.getWidth(), (int) first.getHeight());
for (int i = 1; i < sizes.size(); i++) { 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 @Override

View File

@ -31,7 +31,6 @@ import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface; import android.view.Surface;
import android.view.WindowInsets; import android.view.WindowInsets;
import android.view.WindowManager; 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.DotRenderer;
import com.android.launcher3.icons.GraphicsUtils; import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer; import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info; import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.WindowBounds;
@ -52,9 +50,6 @@ import java.io.PrintWriter;
@SuppressLint("NewApi") @SuppressLint("NewApi")
public class DeviceProfile { 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; private static final int DEFAULT_DOT_SIZE = 100;
public final InvariantDeviceProfile inv; public final InvariantDeviceProfile inv;
@ -63,9 +58,9 @@ public class DeviceProfile {
// Device properties // Device properties
public final boolean isTablet; public final boolean isTablet;
public final boolean isLargeTablet;
public final boolean isPhone; public final boolean isPhone;
public final boolean transposeLayoutWithOrientation; public final boolean transposeLayoutWithOrientation;
public final boolean isTwoPanels;
// Device properties in current orientation // Device properties in current orientation
public final boolean isLandscape; public final boolean isLandscape;
@ -164,6 +159,7 @@ public class DeviceProfile {
public int allAppsCellWidthPx; public int allAppsCellWidthPx;
public int allAppsIconSizePx; public int allAppsIconSizePx;
public int allAppsIconDrawablePaddingPx; public int allAppsIconDrawablePaddingPx;
public final int numShownAllAppsColumns;
public float allAppsIconTextSizePx; public float allAppsIconTextSizePx;
// Overview // Overview
@ -194,42 +190,30 @@ public class DeviceProfile {
// How much of the bottom inset is due to Taskbar rather than other system elements. // How much of the bottom inset is due to Taskbar rather than other system elements.
public int nonOverlappingTaskbarInset; public int nonOverlappingTaskbarInset;
DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
Point minSize, Point maxSize, int width, int height, boolean isLandscape,
boolean isMultiWindowMode, boolean transposeLayoutWithOrientation, boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
Point windowPosition) { boolean useTwoPanels) {
this.inv = inv; this.inv = inv;
this.isLandscape = isLandscape; this.isLandscape = windowBounds.isLandscape();
this.isMultiWindowMode = isMultiWindowMode; this.isMultiWindowMode = isMultiWindowMode;
this.transposeLayoutWithOrientation = transposeLayoutWithOrientation; this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
windowX = windowPosition.x; windowX = windowBounds.bounds.left;
windowY = windowPosition.y; windowY = windowBounds.bounds.top;
isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode; isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
// Determine sizes. // Determine sizes.
widthPx = width; widthPx = windowBounds.bounds.width();
heightPx = height; heightPx = windowBounds.bounds.height();
int nonFinalAvailableHeightPx; availableWidthPx = windowBounds.availableSize.x;
if (isLandscape) { int nonFinalAvailableHeightPx = windowBounds.availableSize.y;
availableWidthPx = maxSize.x;
nonFinalAvailableHeightPx = minSize.y;
} else {
availableWidthPx = minSize.x;
nonFinalAvailableHeightPx = maxSize.y;
}
mInfo = info; 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); aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0; boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@ -284,7 +268,7 @@ public class DeviceProfile {
? 0 ? 0
: res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding); : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get() && isTablet) { if (isTwoPanels) {
cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightPx =
res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding); res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
cellLayoutBottomPaddingPx = 0; cellLayoutBottomPaddingPx = 0;
@ -309,7 +293,10 @@ public class DeviceProfile {
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x); 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 = hotseatBarTopPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding); res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
hotseatBarBottomPaddingPx = (isTallDevice ? 0 hotseatBarBottomPaddingPx = (isTallDevice ? 0
@ -393,11 +380,12 @@ public class DeviceProfile {
} }
public Builder toBuilder(Context context) { 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) return new Builder(context, inv, mInfo)
.setSizeRange(size, size) .setWindowBounds(bounds)
.setSize(widthPx, heightPx) .setUseTwoPanels(isTwoPanels)
.setWindowPosition(windowX, windowY)
.setMultiWindowMode(isMultiWindowMode); .setMultiWindowMode(isMultiWindowMode);
} }
@ -409,15 +397,8 @@ public class DeviceProfile {
* TODO: Move this to the builder as part of setMultiWindowMode * TODO: Move this to the builder as part of setMultiWindowMode
*/ */
public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) { 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) DeviceProfile profile = toBuilder(context)
.setSizeRange(mwSize, mwSize) .setWindowBounds(windowBounds)
.setSize(windowBounds.bounds.width(), windowBounds.bounds.height())
.setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top)
.setMultiWindowMode(true) .setMultiWindowMode(true)
.build(); .build();
@ -433,14 +414,6 @@ public class DeviceProfile {
return profile; 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. * Checks if there is enough space for labels on the workspace.
* If there is not, labels on the Workspace are hidden. * If there is not, labels on the Workspace are hidden.
@ -553,7 +526,7 @@ public class DeviceProfile {
} }
// All apps // All apps
if (allAppsHasDifferentNumColumns()) { if (numShownAllAppsColumns != inv.numColumns) {
allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics); allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics);
allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mMetrics); allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mMetrics);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx; allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
@ -667,18 +640,20 @@ public class DeviceProfile {
} }
public Point getCellSize() { public Point getCellSize() {
return getCellSize(inv.numColumns, inv.numRows); return getCellSize(null);
} }
private Point getCellSize(int numColumns, int numRows) { public Point getCellSize(Point result) {
Point result = new Point(); if (result == null) {
result = new Point();
}
// Since we are only concerned with the overall padding, layout direction does // Since we are only concerned with the overall padding, layout direction does
// not matter. // not matter.
Point padding = getTotalWorkspacePadding(); Point padding = getTotalWorkspacePadding();
result.x = calculateCellWidth(availableWidthPx - padding.x result.x = calculateCellWidth(availableWidthPx - padding.x
- cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, numColumns); - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y result.y = calculateCellHeight(availableHeightPx - padding.y
- cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, numRows); - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
return result; return result;
} }
@ -723,7 +698,7 @@ public class DeviceProfile {
padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2, padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2); availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get()) { if (isTwoPanels) {
padding.set(0, padding.top, 0, padding.bottom); padding.set(0, padding.top, 0, padding.bottom);
} }
} else { } 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 // for this, we pad the left and right of the hotseat with half of the difference of a
// workspace cell vs a hotseat cell. // workspace cell vs a hotseat cell.
float workspaceCellWidth = (float) widthPx / inv.numColumns; float workspaceCellWidth = (float) widthPx / inv.numColumns;
float hotseatCellWidth = (float) widthPx / inv.numShownHotseatIcons; float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2); int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
mHotseatPadding.set( mHotseatPadding.set(
hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
@ -801,13 +776,6 @@ public class DeviceProfile {
return isLandscape && transposeLayoutWithOrientation; 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. * Updates orientation information and returns true if it has changed from the previous value.
*/ */
@ -828,7 +796,7 @@ public class DeviceProfile {
} }
public boolean shouldFadeAdjacentWorkspaceScreens() { public boolean shouldFadeAdjacentWorkspaceScreens() {
return isVerticalBarLayout() || isLargeTablet; return isVerticalBarLayout();
} }
public int getCellHeight(@ContainerType int containerType) { public int getCellHeight(@ContainerType int containerType) {
@ -854,13 +822,13 @@ public class DeviceProfile {
writer.println(prefix + "\t1 dp = " + mMetrics.density + " px"); writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
writer.println(prefix + "\tisTablet:" + isTablet); writer.println(prefix + "\tisTablet:" + isTablet);
writer.println(prefix + "\tisLargeTablet:" + isLargeTablet);
writer.println(prefix + "\tisPhone:" + isPhone); writer.println(prefix + "\tisPhone:" + isPhone);
writer.println(prefix + "\ttransposeLayoutWithOrientation:" writer.println(prefix + "\ttransposeLayoutWithOrientation:"
+ transposeLayoutWithOrientation); + transposeLayoutWithOrientation);
writer.println(prefix + "\tisLandscape:" + isLandscape); writer.println(prefix + "\tisLandscape:" + isLandscape);
writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode); writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
writer.println(prefix + "\tisTwoPanels:" + isTwoPanels);
writer.println(prefix + pxToDpStr("windowX", windowX)); writer.println(prefix + pxToDpStr("windowX", windowX));
writer.println(prefix + pxToDpStr("windowY", windowY)); writer.println(prefix + pxToDpStr("windowY", windowY));
@ -907,6 +875,7 @@ public class DeviceProfile {
writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx", writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
allAppsIconDrawablePaddingPx)); allAppsIconDrawablePaddingPx));
writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx)); writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx)); writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx)); writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
@ -916,6 +885,7 @@ public class DeviceProfile {
hotseatBarSidePaddingStartPx)); hotseatBarSidePaddingStartPx));
writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx", writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
hotseatBarSidePaddingEndPx)); hotseatBarSidePaddingEndPx));
writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent); writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
@ -967,41 +937,16 @@ public class DeviceProfile {
private InvariantDeviceProfile mInv; private InvariantDeviceProfile mInv;
private Info mInfo; private Info mInfo;
private final Point mWindowPosition = new Point(); private WindowBounds mWindowBounds;
private Point mMinSize, mMaxSize; private boolean mUseTwoPanels;
private int mWidth, mHeight;
private boolean mIsLandscape;
private boolean mIsMultiWindowMode = false; private boolean mIsMultiWindowMode = false;
private boolean mTransposeLayoutWithOrientation; private Boolean mTransposeLayoutWithOrientation;
public Builder(Context context, InvariantDeviceProfile inv, Info info) { public Builder(Context context, InvariantDeviceProfile inv, Info info) {
mContext = context; mContext = context;
mInv = inv; mInv = inv;
mInfo = info; 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) { public Builder setMultiWindowMode(boolean isMultiWindowMode) {
@ -1009,11 +954,14 @@ public class DeviceProfile {
return this; return this;
} }
/** public Builder setUseTwoPanels(boolean useTwoPanels) {
* Sets the window position if not full-screen mUseTwoPanels = useTwoPanels;
*/ return this;
public Builder setWindowPosition(int x, int y) { }
mWindowPosition.set(x, y);
public Builder setWindowBounds(WindowBounds bounds) {
mWindowBounds = bounds;
return this; return this;
} }
@ -1023,9 +971,14 @@ public class DeviceProfile {
} }
public DeviceProfile build() { public DeviceProfile build() {
return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize, if (mWindowBounds == null) {
mWidth, mHeight, mIsLandscape, mIsMultiWindowMode, throw new IllegalArgumentException("Window bounds not set");
mTransposeLayoutWithOrientation, mWindowPosition); }
if (mTransposeLayoutWithOrientation == null) {
mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
}
return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds,
mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels);
} }
} }

View File

@ -90,11 +90,11 @@ public class Hotseat extends CellLayout implements Insettable {
public void resetLayout(boolean hasVerticalHotseat) { public void resetLayout(boolean hasVerticalHotseat) {
removeAllViewsInLayout(); removeAllViewsInLayout();
mHasVerticalHotseat = hasVerticalHotseat; mHasVerticalHotseat = hasVerticalHotseat;
InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv; DeviceProfile dp = mActivity.getDeviceProfile();
if (hasVerticalHotseat) { if (hasVerticalHotseat) {
setGridSize(1, idp.numShownHotseatIcons); setGridSize(1, dp.numShownHotseatIcons);
} else { } else {
setGridSize(idp.numShownHotseatIcons, 1); setGridSize(dp.numShownHotseatIcons, 1);
} }
} }

View File

@ -16,11 +16,12 @@
package com.android.launcher3; package com.android.launcher3;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.Utilities.getDevicePrefs; import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.Utilities.getPointString; import static com.android.launcher3.Utilities.getPointString;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME; 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_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.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter; 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.IntArray;
import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.launcher3.util.WindowBounds;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
@ -62,6 +64,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
public class InvariantDeviceProfile { 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_WORKSPACE_SIZE = "migration_src_workspace_size";
public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count"; 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 String KEY_IDP_GRID_NAME = "idp_grid_name";
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48; 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. * Number of columns in the all apps list.
*/ */
public int numAllAppsColumns; public int numAllAppsColumns;
public int numDatabaseAllAppsColumns;
/** /**
* Do not query directly. see {@link DeviceProfile#isScalableGrid}. * Do not query directly. see {@link DeviceProfile#isScalableGrid}.
@ -147,8 +154,7 @@ public class InvariantDeviceProfile {
public int defaultLayoutId; public int defaultLayoutId;
int demoModeLayoutId; int demoModeLayoutId;
public DeviceProfile landscapeProfile; public final List<DeviceProfile> supportedProfiles = new ArrayList<>();
public DeviceProfile portraitProfile;
@Nullable public DevicePaddings devicePaddings; @Nullable public DevicePaddings devicePaddings;
@ -175,6 +181,7 @@ public class InvariantDeviceProfile {
numShownHotseatIcons = p.numShownHotseatIcons; numShownHotseatIcons = p.numShownHotseatIcons;
numDatabaseHotseatIcons = p.numDatabaseHotseatIcons; numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns; numAllAppsColumns = p.numAllAppsColumns;
numDatabaseAllAppsColumns = p.numDatabaseAllAppsColumns;
isScalable = p.isScalable; isScalable = p.isScalable;
devicePaddingId = p.devicePaddingId; devicePaddingId = p.devicePaddingId;
minCellHeight = p.minCellHeight; minCellHeight = p.minCellHeight;
@ -204,7 +211,7 @@ public class InvariantDeviceProfile {
DisplayController.INSTANCE.get(context).addChangeListener( DisplayController.INSTANCE.get(context).addChangeListener(
(displayContext, info, flags) -> { (displayContext, info, flags) -> {
if ((flags & (CHANGE_SIZE | CHANGE_DENSITY)) != 0) { if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
onConfigChanged(displayContext); onConfigChanged(displayContext);
} }
}); });
@ -232,35 +239,29 @@ public class InvariantDeviceProfile {
// Get the display info based on default display and interpolate it to existing display // Get the display info based on default display and interpolate it to existing display
DisplayOption defaultDisplayOption = invDistWeightedInterpolate( DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
DisplayController.INSTANCE.get(context).getInfo(), DisplayController.INSTANCE.get(context).getInfo(),
getPredefinedDeviceProfiles(context, gridName)); getPredefinedDeviceProfiles(context, gridName, false), false);
Info myInfo = new Info(context, display); Info myInfo = new Info(context, display);
DisplayOption myDisplayOption = invDistWeightedInterpolate( DisplayOption myDisplayOption = invDistWeightedInterpolate(
myInfo, getPredefinedDeviceProfiles(context, gridName)); myInfo, getPredefinedDeviceProfiles(context, gridName, false), false);
DisplayOption result = new DisplayOption(defaultDisplayOption.grid) DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
.add(myDisplayOption); .add(myDisplayOption);
result.iconSize = defaultDisplayOption.iconSize; result.iconSize = defaultDisplayOption.iconSize;
result.landscapeIconSize = defaultDisplayOption.landscapeIconSize; result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
result.numShownHotseatIcons = myDisplayOption.numShownHotseatIcons;
if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) { if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
result.allAppsIconSize = defaultDisplayOption.allAppsIconSize; result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns;
} else { } else {
result.allAppsIconSize = myDisplayOption.allAppsIconSize; result.allAppsIconSize = myDisplayOption.allAppsIconSize;
result.numAllAppsColumns = myDisplayOption.numAllAppsColumns;
} }
result.minCellHeight = defaultDisplayOption.minCellHeight; result.minCellHeight = defaultDisplayOption.minCellHeight;
result.minCellWidth = defaultDisplayOption.minCellWidth; result.minCellWidth = defaultDisplayOption.minCellWidth;
result.borderSpacing = defaultDisplayOption.borderSpacing; result.borderSpacing = defaultDisplayOption.borderSpacing;
initGrid(context, myInfo, result); initGrid(context, myInfo, result, false);
} }
public static String getCurrentGridName(Context context) { public static String getCurrentGridName(Context context) {
if (ENABLE_TWO_PANEL_HOME.get()) {
return ENABLE_TWO_PANEL_HOME.key;
}
return Utilities.isGridOptionsEnabled(context) return Utilities.isGridOptionsEnabled(context)
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null; ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
} }
@ -278,20 +279,33 @@ public class InvariantDeviceProfile {
private String initGrid(Context context, String gridName) { private String initGrid(Context context, String gridName) {
Info displayInfo = DisplayController.INSTANCE.get(context).getInfo(); Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName); // Determine if we have split display
DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions); boolean isTablet = false, isPhone = false;
initGrid(context, displayInfo, displayOption); for (WindowBounds bounds : displayInfo.supportedBounds) {
if (displayInfo.isTablet(bounds)) {
isTablet = true;
} else {
isPhone = true;
}
}
boolean isSplitDisplay = isPhone && isTablet && ENABLE_TWO_PANEL_HOME.get();
ArrayList<DisplayOption> allOptions =
getPredefinedDeviceProfiles(context, gridName, isSplitDisplay);
DisplayOption displayOption =
invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay);
initGrid(context, displayInfo, displayOption, isSplitDisplay);
return displayOption.grid.name; return displayOption.grid.name;
} }
private void initGrid( private void initGrid(
Context context, Info displayInfo, DisplayOption displayOption) { Context context, Info displayInfo, DisplayOption displayOption,
boolean isSplitDisplay) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics(); DisplayMetrics metrics = context.getResources().getDisplayMetrics();
GridOption closestProfile = displayOption.grid; GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows; numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns; numColumns = closestProfile.numColumns;
numDatabaseHotseatIcons = closestProfile.numDatabaseHotseatIcons;
dbFile = closestProfile.dbFile; dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId; defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId; demoModeLayoutId = closestProfile.demoModeLayoutId;
@ -313,8 +327,14 @@ public class InvariantDeviceProfile {
minCellHeight = displayOption.minCellHeight; minCellHeight = displayOption.minCellHeight;
minCellWidth = displayOption.minCellWidth; minCellWidth = displayOption.minCellWidth;
borderSpacing = displayOption.borderSpacing; 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)) { if (Utilities.isGridOptionsEnabled(context)) {
allAppsIconSize = displayOption.allAppsIconSize; allAppsIconSize = displayOption.allAppsIconSize;
@ -332,31 +352,26 @@ public class InvariantDeviceProfile {
// Supported overrides: numRows, numColumns, iconSize // Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, metrics); applyPartnerDeviceProfileOverrides(context, metrics);
Point realSize = new Point(displayInfo.realSize); supportedProfiles.clear();
// The real size never changes. smallSide and largeSide will remain the defaultWallpaperSize = new Point(displayInfo.currentSize);
// same in any orientation. for (WindowBounds bounds : displayInfo.supportedBounds) {
int smallSide = Math.min(realSize.x, realSize.y); supportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
int largeSide = Math.max(realSize.x, realSize.y); .setUseTwoPanels(isSplitDisplay)
.setWindowBounds(bounds).build());
DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo) // Wallpaper size should be the maximum of the all possible sizes Launcher expects
.setSizeRange(new Point(displayInfo.smallestSize), int displayWidth = bounds.bounds.width();
new Point(displayInfo.largestSize)); int displayHeight = bounds.bounds.height();
if (TestProtocol.sDebugTracing) { defaultWallpaperSize.y = Math.max(defaultWallpaperSize.y, displayHeight);
Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
"largeSide=" + largeSide + " smallSide=" + smallSide);
}
landscapeProfile = builder.setSize(largeSide, smallSide).build(); // We need to ensure that there is enough extra space in the wallpaper
portraitProfile = builder.setSize(smallSide, largeSide).build(); // for the intended parallax effects
float parallaxFactor =
// We need to ensure that there is enough extra space in the wallpaper dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720
// for the intended parallax effects ? 2
if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) { : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight);
defaultWallpaperSize = new Point( defaultWallpaperSize.x =
(int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)), Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth));
largeSide);
} else {
defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
} }
ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName()); ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
@ -385,7 +400,7 @@ public class InvariantDeviceProfile {
} else if (!savedIconMaskPath.equals(getIconShapePath(context))) { } else if (!savedIconMaskPath.equals(getIconShapePath(context))) {
getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context)) getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
.apply(); .apply();
apply(context, CHANGE_FLAG_ICON_PARAMS); apply(CHANGE_FLAG_ICON_PARAMS);
} }
} }
@ -423,16 +438,17 @@ public class InvariantDeviceProfile {
IconShape.init(context); IconShape.init(context);
} }
apply(context, changeFlags); apply(changeFlags);
} }
private void apply(Context context, int changeFlags) { private void apply(int changeFlags) {
for (OnIDPChangeListener listener : mChangeListeners) { for (OnIDPChangeListener listener : mChangeListeners) {
listener.onIdpChanged(changeFlags, this); listener.onIdpChanged(changeFlags, this);
} }
} }
static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) { private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(
Context context, String gridName, boolean isSplitDisplay) {
ArrayList<DisplayOption> profiles = new ArrayList<>(); ArrayList<DisplayOption> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) { try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth(); final int depth = parser.getDepth();
@ -449,8 +465,9 @@ public class InvariantDeviceProfile {
&& type != XmlPullParser.END_DOCUMENT) { && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) && "display-option".equals( if ((type == XmlPullParser.START_TAG) && "display-option".equals(
parser.getName())) { parser.getName())) {
profiles.add(new DisplayOption( profiles.add(new DisplayOption(gridOption, context,
gridOption, context, Xml.asAttributeSet(parser))); Xml.asAttributeSet(parser),
isSplitDisplay ? DEFAULT_SPLIT_DISPLAY : DEFAULT_TRUE));
} }
} }
} }
@ -521,17 +538,29 @@ public class InvariantDeviceProfile {
return (float) Math.hypot(x1 - x0, y1 - y0); return (float) Math.hypot(x1 - x0, y1 - y0);
} }
@VisibleForTesting private static DisplayOption invDistWeightedInterpolate(
static DisplayOption invDistWeightedInterpolate( Info displayInfo, ArrayList<DisplayOption> points, boolean isSplitDisplay) {
Info displayInfo, ArrayList<DisplayOption> points) { int minWidthPx = Integer.MAX_VALUE;
Point smallestSize = new Point(displayInfo.smallestSize); int minHeightPx = Integer.MAX_VALUE;
Point largestSize = new Point(displayInfo.largestSize); 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 } else if (!isTablet && bounds.isLandscape()) {
float width = Utilities.dpiFromPx((float) Math.min(smallestSize.x, smallestSize.y), // We will use transposed layout in this case
displayInfo.densityDpi); minWidthPx = Math.min(minWidthPx, bounds.availableSize.y);
float height = Utilities.dpiFromPx((float) Math.min(largestSize.x, largestSize.y), minHeightPx = Math.min(minHeightPx, bounds.availableSize.x);
displayInfo.densityDpi); } 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 // Sort the profiles based on the closeness to the device size
Collections.sort(points, (a, b) -> Collections.sort(points, (a, b) ->
@ -556,33 +585,30 @@ public class InvariantDeviceProfile {
return out.multiply(1.0f / weights); return out.multiply(1.0f / weights);
} }
@VisibleForTesting
static DisplayOption invDistWeightedInterpolate(float width, float height,
ArrayList<DisplayOption> points) {
float weights = 0;
DisplayOption p = points.get(0);
if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
return p;
}
DisplayOption out = new DisplayOption();
for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
p = points.get(i);
float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
weights += w;
out.add(new DisplayOption().add(p).multiply(w));
}
return out.multiply(1.0f / weights);
}
public DeviceProfile getDeviceProfile(Context context) { 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) { if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "getDeviceProfile: orientation=" Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
+ context.getResources().getConfiguration().orientation); "getDeviceProfile: orientation=" + config.orientation
+ " size=" + availableWidth + "x" + availableHeight);
} }
return context.getResources().getConfiguration().orientation DeviceProfile bestMatch = supportedProfiles.get(0);
== Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile; 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) { 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 numFolderRows;
private final int numFolderColumns; private final int numFolderColumns;
private final int numAllAppsColumns;
private final int numDatabaseAllAppsColumns;
private final int numHotseatIcons;
private final int numDatabaseHotseatIcons; private final int numDatabaseHotseatIcons;
private final String dbFile; private final String dbFile;
@ -649,8 +678,6 @@ public class InvariantDeviceProfile {
private final boolean isScalable; private final boolean isScalable;
private final int devicePaddingId; private final int devicePaddingId;
public final boolean visible;
private final SparseArray<TypedValue> extraAttrs; private final SparseArray<TypedValue> extraAttrs;
public GridOption(Context context, AttributeSet attrs) { public GridOption(Context context, AttributeSet attrs) {
@ -665,8 +692,17 @@ public class InvariantDeviceProfile {
R.styleable.GridDisplayOption_defaultLayoutId, 0); R.styleable.GridDisplayOption_defaultLayoutId, 0);
demoModeLayoutId = a.getResourceId( demoModeLayoutId = a.getResourceId(
R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId); 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); R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
numDatabaseHotseatIcons = a.getInt(
R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
numFolderRows = a.getInt( numFolderRows = a.getInt(
R.styleable.GridDisplayOption_numFolderRows, numRows); R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt( numFolderColumns = a.getInt(
@ -677,24 +713,21 @@ public class InvariantDeviceProfile {
devicePaddingId = a.getResourceId( devicePaddingId = a.getResourceId(
R.styleable.GridDisplayOption_devicePaddingId, 0); R.styleable.GridDisplayOption_devicePaddingId, 0);
visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true);
a.recycle(); a.recycle();
extraAttrs = Themes.createValueMap(context, attrs, extraAttrs = Themes.createValueMap(context, attrs,
IntArray.wrap(R.styleable.GridDisplayOption)); IntArray.wrap(R.styleable.GridDisplayOption));
} }
} }
private static final class DisplayOption { @VisibleForTesting
private final GridOption grid; static final class DisplayOption {
public final GridOption grid;
private final float minWidthDps; private final float minWidthDps;
private final float minHeightDps; private final float minHeightDps;
private final boolean canBeDefault; private final boolean canBeDefault;
private float numShownHotseatIcons;
private float numAllAppsColumns;
private float minCellHeight; private float minCellHeight;
private float minCellWidth; private float minCellWidth;
private float borderSpacing; private float borderSpacing;
@ -706,7 +739,7 @@ public class InvariantDeviceProfile {
private float allAppsIconSize; private float allAppsIconSize;
private float allAppsIconTextSize; private float allAppsIconTextSize;
DisplayOption(GridOption grid, Context context, AttributeSet attrs) { DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) {
this.grid = grid; this.grid = grid;
TypedArray a = context.obtainStyledAttributes( TypedArray a = context.obtainStyledAttributes(
@ -714,12 +747,9 @@ public class InvariantDeviceProfile {
minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0); minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0); minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
canBeDefault = a.getBoolean(
R.styleable.ProfileDisplayOption_canBeDefault, false); canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0)
numShownHotseatIcons = a.getInt(R.styleable.ProfileDisplayOption_numShownHotseatIcons, == defaultFlagValue;
grid.numDatabaseHotseatIcons);
numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns,
grid.numColumns);
minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0); minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0); minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
@ -748,16 +778,12 @@ public class InvariantDeviceProfile {
minWidthDps = 0; minWidthDps = 0;
minHeightDps = 0; minHeightDps = 0;
canBeDefault = false; canBeDefault = false;
numShownHotseatIcons = 0;
numAllAppsColumns = 0;
minCellHeight = 0; minCellHeight = 0;
minCellWidth = 0; minCellWidth = 0;
borderSpacing = 0; borderSpacing = 0;
} }
private DisplayOption multiply(float w) { private DisplayOption multiply(float w) {
numShownHotseatIcons *= w;
numAllAppsColumns *= w;
iconSize *= w; iconSize *= w;
landscapeIconSize *= w; landscapeIconSize *= w;
allAppsIconSize *= w; allAppsIconSize *= w;
@ -771,8 +797,6 @@ public class InvariantDeviceProfile {
} }
private DisplayOption add(DisplayOption p) { private DisplayOption add(DisplayOption p) {
numShownHotseatIcons += p.numShownHotseatIcons;
numAllAppsColumns += p.numAllAppsColumns;
iconSize += p.iconSize; iconSize += p.iconSize;
landscapeIconSize += p.landscapeIconSize; landscapeIconSize += p.landscapeIconSize;
allAppsIconSize += p.allAppsIconSize; allAppsIconSize += p.allAppsIconSize;

View File

@ -189,7 +189,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler; import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.WidgetManagerHelper;
import com.android.launcher3.widget.custom.CustomWidgetManager; import com.android.launcher3.widget.custom.CustomWidgetManager;
import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@ -2332,8 +2331,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
pendingInfo.spanY = item.spanY; pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY; pendingInfo.minSpanY = item.minSpanY;
Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, Bundle options = pendingInfo.getDefaultSizeOptions(this);
pendingInfo);
boolean isDirectConfig = boolean isDirectConfig =
item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG); item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);

View File

@ -456,7 +456,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
} }
private boolean isTwoPanelEnabled() { private boolean isTwoPanelEnabled() {
return mLauncher.mDeviceProfile.isTablet && FeatureFlags.ENABLE_TWO_PANEL_HOME.get(); return mLauncher.mDeviceProfile.isTwoPanels;
} }
@Override @Override

View File

@ -157,7 +157,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
@Override @Override
public void onDeviceProfileChanged(DeviceProfile dp) { public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) { for (AdapterHolder holder : mAH) {
holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns); holder.adapter.setAppsPerRow(dp.numShownAllAppsColumns);
if (holder.recyclerView != null) { if (holder.recyclerView != null) {
// Remove all views and clear the pool, while keeping the data same. After this // Remove all views and clear the pool, while keeping the data same. After this
// call, all the viewHolders will be recreated. // call, all the viewHolders will be recreated.

View File

@ -283,7 +283,7 @@ public class AllAppsGridAdapter extends
mOnIconClickListener = launcher.getItemOnClickListener(); mOnIconClickListener = launcher.getItemOnClickListener();
mSearchAdapterProvider = searchAdapterProvider; mSearchAdapterProvider = searchAdapterProvider;
setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns); setAppsPerRow(mLauncher.getDeviceProfile().numShownAllAppsColumns);
} }
public void setAppsPerRow(int appsPerRow) { public void setAppsPerRow(int appsPerRow) {

View File

@ -64,7 +64,6 @@ import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetCell; import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetCellPreview; import com.android.launcher3.widget.WidgetCellPreview;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView; import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.WidgetManagerHelper;
@ -234,7 +233,7 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo); PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX); pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY); pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); mWidgetOptions = pendingInfo.getDefaultSizeOptions(this);
mWidgetCell.getWidgetView().setTag(pendingInfo); mWidgetCell.getWidgetView().setTag(pendingInfo);
applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache())); applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));

View File

@ -90,10 +90,7 @@ public class GridCustomizationsProvider extends ContentProvider {
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) if ((type == XmlPullParser.START_TAG)
&& GridOption.TAG_NAME.equals(parser.getName())) { && GridOption.TAG_NAME.equals(parser.getName())) {
GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser)); result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
if (option.visible) {
result.add(option);
}
} }
} }
} catch (IOException | XmlPullParserException e) { } catch (IOException | XmlPullParserException e) {

View File

@ -224,7 +224,7 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper
mUiHandler = new Handler(Looper.getMainLooper()); mUiHandler = new Handler(Looper.getMainLooper());
mContext = context; mContext = context;
mIdp = idp; mIdp = idp;
mDp = idp.portraitProfile.copy(context); mDp = idp.getDeviceProfile(context).copy(context);
mMigrated = migrated; mMigrated = migrated;
// TODO: get correct insets once display cutout API is available. // TODO: get correct insets once display cutout API is available.

View File

@ -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_ID;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER; 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.Activity;
import android.app.Fragment; import android.app.Fragment;
@ -32,11 +32,9 @@ import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.SizeF;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -45,7 +43,6 @@ import android.widget.FrameLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R; import com.android.launcher3.R;
@ -53,8 +50,6 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.FragmentWithPreview; 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 * 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)}. * 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"; protected String mKeyWidgetId = "qsb_widget_id";
private QsbWidgetHost mQsbWidgetHost; private QsbWidgetHost mQsbWidgetHost;
private AppWidgetProviderInfo mWidgetInfo; protected AppWidgetProviderInfo mWidgetInfo;
private QsbWidgetHostView mQsb; private QsbWidgetHostView mQsb;
// We need to store the orientation here, due to a bug (b/64916689) that results in widgets // 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() { protected Bundle createBindOptions() {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext()); InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
return getWidgetSizeOptions(getContext(), mWidgetInfo.provider, idp.numColumns, 1);
Bundle opts = new Bundle();
ArrayList<SizeF> sizes = AppWidgetResizeFrame
.getWidgetSizes(getContext(), idp.numColumns, 1);
Rect size = AppWidgetResizeFrame.getMinMaxSizes(sizes, null /* outRect */);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
if (ATLEAST_S) {
opts.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes);
}
return opts;
} }
protected View getDefaultView(ViewGroup container, boolean showSetupIcon) { protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {

View File

@ -87,8 +87,8 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
if (mDragLayer != null) { if (mDragLayer != null) {
return; return;
} }
InvariantDeviceProfile currentDisplayIdp = InvariantDeviceProfile currentDisplayIdp = new InvariantDeviceProfile(
new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay()); this, getWindow().getDecorView().getDisplay());
// Disable transpose layout and use multi-window mode so that the icons are scaled properly // Disable transpose layout and use multi-window mode so that the icons are scaled properly
mDeviceProfile = currentDisplayIdp.getDeviceProfile(this) mDeviceProfile = currentDisplayIdp.getDeviceProfile(this)

View File

@ -29,7 +29,6 @@ import android.widget.GridView;
import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
@ -109,8 +108,6 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
setMeasuredDimension(width, height); setMeasuredDimension(width, height);
DeviceProfile grid = mActivity.getDeviceProfile(); DeviceProfile grid = mActivity.getDeviceProfile();
InvariantDeviceProfile idp = grid.inv;
int count = getChildCount(); int count = getChildCount();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
final View child = getChildAt(i); final View child = getChildAt(i);
@ -118,10 +115,10 @@ public class SecondaryDragLayer extends BaseDragLayer<SecondaryDisplayLauncher>
int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
+ grid.cellLayoutPaddingLeftRightPx); + grid.cellLayoutPaddingLeftRightPx);
int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding; int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding;
int appsWidth = Math.min(width, maxWidth); 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); int appsHeight = Math.min(height, maxHeight);
mAppsView.measure( mAppsView.measure(

View File

@ -18,8 +18,10 @@ package com.android.launcher3.util;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 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.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_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.SuppressLint;
import android.annotation.TargetApi; import android.annotation.TargetApi;
@ -32,16 +34,22 @@ import android.graphics.Point;
import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Build; import android.os.Build;
import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import android.view.Display; import android.view.Display;
import android.view.WindowMetrics;
import androidx.annotation.AnyThread; import androidx.annotation.AnyThread;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.ApiWrapper;
import java.util.ArrayList; 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. * 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<DisplayController> INSTANCE = public static final MainThreadInitializedObject<DisplayController> INSTANCE =
new MainThreadInitializedObject<>(DisplayController::new); 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_ROTATION = 1 << 1;
public static final int CHANGE_FRAME_DELAY = 1 << 2; public static final int CHANGE_FRAME_DELAY = 1 << 2;
public static final int CHANGE_DENSITY = 1 << 3; 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 public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
| CHANGE_FRAME_DELAY | CHANGE_DENSITY; | CHANGE_FRAME_DELAY | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
private final Context mContext; private final Context mContext;
private final DisplayManager mDM; private final DisplayManager mDM;
@ -87,7 +96,22 @@ public class DisplayController implements DisplayListener, ComponentCallbacks {
new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)); new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
} }
mInfo = new Info(getContext(display), display); // Create a single holder for all internal displays. External display holders created
// lazily.
Set<PortraitSize> extraInternalDisplays = new ArraySet<>();
for (Display d : mDM.getDisplays()) {
if (ApiWrapper.isInternalDisplay(display) && d.getDisplayId() != DEFAULT_DISPLAY) {
Point size = new Point();
d.getRealSize(size);
extraInternalDisplays.add(new PortraitSize(size.x, size.y));
}
}
if (extraInternalDisplays.isEmpty() || !Utilities.ATLEAST_S) {
mInfo = new Info(createDisplayInfoContext(display), display, Collections.emptySet());
} else {
mInfo = new Info(mWindowContext, display, extraInternalDisplays);
}
mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler()); mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
} }
@ -139,7 +163,7 @@ public class DisplayController implements DisplayListener, ComponentCallbacks {
*/ */
private void onConfigChanged(Intent intent) { private void onConfigChanged(Intent intent) {
Configuration config = mContext.getResources().getConfiguration(); 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"); Log.d(TAG, "Configuration changed, notifying listeners");
Display display = mDM.getDisplay(DEFAULT_DISPLAY); Display display = mDM.getDisplay(DEFAULT_DISPLAY);
if (display != null) { if (display != null) {
@ -157,8 +181,7 @@ public class DisplayController implements DisplayListener, ComponentCallbacks {
|| config.fontScale != mInfo.fontScale || config.fontScale != mInfo.fontScale
|| display.getRotation() != mInfo.rotation || display.getRotation() != mInfo.rotation
|| !mInfo.mScreenSizeDp.equals( || !mInfo.mScreenSizeDp.equals(
Math.min(config.screenHeightDp, config.screenWidthDp), new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
Math.max(config.screenHeightDp, config.screenWidthDp))) {
handleInfoChange(display); handleInfoChange(display);
} }
} }
@ -178,18 +201,23 @@ public class DisplayController implements DisplayListener, ComponentCallbacks {
return mInfo; return mInfo;
} }
private Context getContext(Display display) { private Context createDisplayInfoContext(Display display) {
return Utilities.ATLEAST_S ? mWindowContext : mContext.createDisplayContext(display); return Utilities.ATLEAST_S
? mContext.createWindowContext(display, TYPE_APPLICATION, null)
: mContext.createDisplayContext(display);
} }
@AnyThread @AnyThread
private void handleInfoChange(Display display) { private void handleInfoChange(Display display) {
Info oldInfo = mInfo; Info oldInfo = mInfo;
Context context = getContext(display); Set<PortraitSize> extraDisplaysSizes = oldInfo.mAllSizes.size() > 1
Info newInfo = new Info(context, display); ? oldInfo.mAllSizes : Collections.emptySet();
Context displayContext = createDisplayInfoContext(display);
Info newInfo = new Info(displayContext, display, extraDisplaysSizes);
int change = 0; int change = 0;
if (newInfo.hasDifferentSize(oldInfo)) { if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) {
change |= CHANGE_SIZE; change |= CHANGE_ACTIVE_SCREEN;
} }
if (newInfo.rotation != oldInfo.rotation) { if (newInfo.rotation != oldInfo.rotation) {
change |= CHANGE_ROTATION; change |= CHANGE_ROTATION;
@ -200,11 +228,14 @@ public class DisplayController implements DisplayListener, ComponentCallbacks {
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) { if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
change |= CHANGE_DENSITY; change |= CHANGE_DENSITY;
} }
if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
change |= CHANGE_SUPPORTED_BOUNDS;
}
if (change != 0) { if (change != 0) {
mInfo = newInfo; mInfo = newInfo;
final int flags = change; 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 float fontScale;
public final int densityDpi; public final int densityDpi;
private final Point mScreenSizeDp; private final PortraitSize mScreenSizeDp;
private final Set<PortraitSize> mAllSizes;
public final Point realSize; public final Point currentSize;
public final Point smallestSize;
public final Point largestSize; public final Set<WindowBounds> supportedBounds = new ArraySet<>();
public Info(Context context, Display display) { public Info(Context context, Display display) {
this(context, display, Collections.emptySet());
}
private Info(Context context, Display display, Set<PortraitSize> extraDisplaysSizes) {
id = display.getDisplayId(); id = display.getDisplayId();
rotation = display.getRotation(); rotation = display.getRotation();
@ -238,35 +274,67 @@ public class DisplayController implements DisplayListener, ComponentCallbacks {
Configuration config = context.getResources().getConfiguration(); Configuration config = context.getResources().getConfiguration();
fontScale = config.fontScale; fontScale = config.fontScale;
densityDpi = config.densityDpi; densityDpi = config.densityDpi;
mScreenSizeDp = new Point( mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
Math.min(config.screenHeightDp, config.screenWidthDp),
Math.max(config.screenHeightDp, config.screenWidthDp));
singleFrameMs = getSingleFrameMs(display); singleFrameMs = getSingleFrameMs(display);
currentSize = new Point();
realSize = new Point(); display.getRealSize(currentSize);
smallestSize = new Point();
largestSize = new Point();
display.getRealSize(realSize); if (extraDisplaysSizes.isEmpty() || !Utilities.ATLEAST_S) {
display.getCurrentSizeRange(smallestSize, largestSize); Point smallestSize = new Point();
Point largestSize = new Point();
display.getCurrentSizeRange(smallestSize, largestSize);
int portraitWidth = Math.min(currentSize.x, currentSize.y);
int portraitHeight = Math.max(currentSize.x, currentSize.y);
supportedBounds.add(new WindowBounds(portraitWidth, portraitHeight,
smallestSize.x, largestSize.y));
supportedBounds.add(new WindowBounds(portraitHeight, portraitWidth,
largestSize.x, smallestSize.y));
mAllSizes = Collections.singleton(new PortraitSize(currentSize.x, currentSize.y));
} else {
mAllSizes = new ArraySet<>(extraDisplaysSizes);
mAllSizes.add(new PortraitSize(currentSize.x, currentSize.y));
Set<WindowMetrics> metrics = WindowManagerCompat.getDisplayProfiles(
context, mAllSizes, densityDpi,
ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
metrics.forEach(wm -> supportedBounds.add(WindowBounds.fromWindowMetrics(wm)));
}
} }
private boolean hasDifferentSize(Info info) { /**
if (!realSize.equals(info.realSize) * Returns true if the bounds represent a tablet
&& !realSize.equals(info.realSize.y, info.realSize.x)) { */
Log.d(TAG, String.format("Display size changed from %s to %s", public boolean isTablet(WindowBounds bounds) {
info.realSize, realSize)); return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
return true; 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]", * Utility class to hold a size information in an orientation independent way
smallestSize, largestSize, info.smallestSize, info.largestSize)); */
return true; 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);
} }
} }

View File

@ -15,11 +15,16 @@
*/ */
package com.android.launcher3.util; package com.android.launcher3.util;
import android.graphics.Insets;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.view.WindowInsets.Type;
import android.view.WindowMetrics;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import java.util.Objects;
/** /**
* Utility class to hold information about window position and layout * Utility class to hold information about window position and layout
*/ */
@ -36,6 +41,18 @@ public class WindowBounds {
bounds.height() - insets.top - insets.bottom); 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 @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
if (!(obj instanceof WindowBounds)) { if (!(obj instanceof WindowBounds)) {
@ -44,4 +61,30 @@ public class WindowBounds {
WindowBounds other = (WindowBounds) obj; WindowBounds other = (WindowBounds) obj;
return other.bounds.equals(bounds) && other.insets.equals(insets); 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));
}
} }

View File

@ -0,0 +1,107 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util;
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.Utilities.dpiFromPx;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Build;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
import android.view.WindowMetrics;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.util.DisplayController.PortraitSize;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* Utility class to simulate window manager APIs until proper APIs are available
*/
@TargetApi(Build.VERSION_CODES.S)
public class WindowManagerCompat {
public static final int MIN_TABLET_WIDTH = 600;
/**
* Returns a set of supported render sizes for a set of internal displays.
* This is a temporary workaround which assumes only nav-bar insets change across displays
* @param consumeTaskBar if true, it assumes that task bar is part of the app window
* and ignores any insets because of task bar.
*/
public static Set<WindowMetrics> getDisplayProfiles(
Context windowContext, Collection<PortraitSize> allDisplaySizes,
int densityDpi, boolean consumeTaskBar) {
WindowInsets metrics = windowContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics().getWindowInsets();
boolean hasNavbar = ResourceUtils.getIntegerByName(
"config_navBarInteractionMode",
windowContext.getResources(),
INVALID_RESOURCE_HANDLE) != 0;
WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics);
Set<WindowMetrics> result = new HashSet<>();
for (PortraitSize size : allDisplaySizes) {
int swDP = (int) dpiFromPx(size.width, densityDpi);
boolean isTablet = swDP >= MIN_TABLET_WIDTH;
final Insets portraitNav, landscapeNav;
if (isTablet && !consumeTaskBar) {
portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
.getDimensionPixelSize(R.dimen.taskbar_size));
} else if (hasNavbar) {
portraitNav = Insets.of(0, 0, 0,
getSystemResource(windowContext, "navigation_bar_height", swDP));
landscapeNav = isTablet
? Insets.of(0, 0, 0, getSystemResource(windowContext,
"navigation_bar_height_landscape", swDP))
: Insets.of(0, 0, getSystemResource(windowContext,
"navigation_bar_width", swDP), 0);
} else {
portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
}
result.add(new WindowMetrics(
new Rect(0, 0, size.width, size.height),
insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build()));
result.add(new WindowMetrics(
new Rect(0, 0, size.height, size.width),
insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build()));
}
return result;
}
private static int getSystemResource(Context context, String key, int swDp) {
int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
Configuration conf = new Configuration();
conf.smallestScreenWidthDp = swDp;
return context.createConfigurationContext(conf)
.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
}

View File

@ -44,7 +44,7 @@ public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView {
mPaint = new TextPaint(); mPaint = new TextPaint();
mPaint.setColor(Color.WHITE); mPaint.setColor(Color.WHITE);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx, mLauncher.getDeviceProfile().iconTextSizePx,
getResources().getDisplayMetrics())); getResources().getDisplayMetrics()));
setBackgroundResource(R.drawable.bg_deferred_app_widget); setBackgroundResource(R.drawable.bg_deferred_app_widget);
} }

View File

@ -13,6 +13,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcel; import android.os.Parcel;
import android.os.UserHandle; import android.os.UserHandle;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
@ -66,20 +67,25 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
} }
public void initSpans(Context context, InvariantDeviceProfile idp) { 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 // Always assume we're working with the smallest span to make sure we
// reserve enough space in both orientations. // reserve enough space in both orientations.
float smallestCellWidth = Math.min(landCellSize.x, portCellSize.x); float smallestCellWidth = Float.MAX_VALUE;
float smallestCellHeight = Math.min(landCellSize.y, portCellSize.y); 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 // 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. // 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. // If grids supports insetting widgets, we do not account for widget padding.
Rect widgetPadding = new Rect(); Rect widgetPadding = new Rect();
if (!idp.landscapeProfile.shouldInsetWidgets() if (isWidgetPadded) {
|| !idp.portraitProfile.shouldInsetWidgets()) {
AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding); AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
} }

View File

@ -15,9 +15,11 @@
*/ */
package com.android.launcher3.widget; package com.android.launcher3.widget;
import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
@ -57,4 +59,8 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo {
public WidgetAddFlowHandler getHandler() { public WidgetAddFlowHandler getHandler() {
return new WidgetAddFlowHandler(info); return new WidgetAddFlowHandler(info);
} }
public Bundle getDefaultSizeOptions(Context context) {
return getWidgetSizeOptions(context, componentName, spanX, spanY);
}
} }

View File

@ -1,16 +1,12 @@
package com.android.launcher3.widget; package com.android.launcher3.widget;
import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.Context; import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import android.util.SizeF;
import android.view.View; import android.view.View;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.DropTarget; import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.dragndrop.DragController; 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.dragndrop.DragOptions;
import com.android.launcher3.util.Thunk; import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
import java.util.stream.Collectors;
public class WidgetHostViewLoader implements DragController.DragListener { public class WidgetHostViewLoader implements DragController.DragListener {
private static final String TAG = "WidgetHostViewLoader"; private static final String TAG = "WidgetHostViewLoader";
private static final boolean LOGD = false; private static final boolean LOGD = false;
@ -90,7 +83,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
if (pInfo.isCustomWidget()) { if (pInfo.isCustomWidget()) {
return false; 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 there is a configuration activity, do not follow thru bound and inflate.
if (mInfo.getHandler().needsConfigure()) { if (mInfo.getHandler().needsConfigure()) {
@ -154,29 +147,4 @@ public class WidgetHostViewLoader implements DragController.DragListener {
return true; return true;
} }
public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
ArrayList<SizeF> sizes = AppWidgetResizeFrame
.getWidgetSizes(context, info.spanX, info.spanY);
Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
info.componentName, null);
float density = context.getResources().getDisplayMetrics().density;
float xPaddingDips = (padding.left + padding.right) / density;
float yPaddingDips = (padding.top + padding.bottom) / density;
ArrayList<SizeF> paddedSizes = sizes.stream().map(
size -> new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips),
Math.max(0.f, size.getHeight() - yPaddingDips))).collect(
Collectors.toCollection(ArrayList::new));
Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes, null /* outRect */);
Bundle options = new Bundle();
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
return options;
}
} }

View File

@ -18,12 +18,22 @@ package com.android.launcher3.uioverrides;
import android.app.Person; import android.app.Person;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.view.Display;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
public class ApiWrapper { public class ApiWrapper {
public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
public static Person[] getPersons(ShortcutInfo si) { public static Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY; 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;
}
} }

View File

@ -16,7 +16,6 @@
package com.android.launcher3.common; package com.android.launcher3.common;
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHost;
import android.content.ContentResolver; import android.content.ContentResolver;
@ -62,7 +61,7 @@ public class WidgetUtils {
pendingInfo.spanY = item.spanY; pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX; pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY; pendingInfo.minSpanY = item.minSpanY;
Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo); Bundle options = pendingInfo.getDefaultSizeOptions(targetContext);
AppWidgetHost host = new LauncherAppWidgetHost(targetContext); AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
int widgetId = host.allocateAppWidgetId(); int widgetId = host.allocateAppWidgetId();