Initial changes to creating a fake landscape Launcher UI
Workspace and hotseat are drawn in rotated UI giving the impression that the
device is in Portrait, even though it is in landscape
Bug: 131360075
Change-Id: I29c4068af25fd4dcf7039b9a45886e864a137977
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 61d329b..50f25fb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -116,7 +116,7 @@
} else {
workspaceView = null;
}
- final Rect iconLocation = new Rect();
+ final RectF iconLocation = new RectF();
boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
final FloatingIconView floatingView = canUseWorkspaceView
? FloatingIconView.getFloatingIconView(activity, workspaceView,
@@ -138,7 +138,7 @@
final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
if (canUseWorkspaceView) {
- return new RectF(iconLocation);
+ return iconLocation;
} else {
// Fallback to animate to center of screen.
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
index 10283bf..2ac61c5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
@@ -52,7 +52,7 @@
private final RecentsView mParent;
private final View mIconView;
- private final int[] mIconPos;
+ private final float[] mIconPos;
private final TaskView mTaskView;
private final TaskThumbnailView mThumbnailView;
@@ -68,7 +68,7 @@
mParent = parent;
mTaskView = tv;
mIconView = tv.getIconView();
- mIconPos = new int[2];
+ mIconPos = new float[2];
mIconScale = mIconView.getScaleX();
Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index a8666f9..e1a115a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -408,7 +408,7 @@
*/
private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
Rect windowTargetBounds, boolean toggleVisibility) {
- Rect bounds = new Rect();
+ RectF bounds = new RectF();
mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
bounds, true /* isOpening */, mFloatingView);
Rect crop = new Rect();
@@ -423,8 +423,8 @@
// Scale the app icon to take up the entire screen. This simplifies the math when
// animating the app window position / scale.
float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
- float maxScaleX = smallestSize / (float) bounds.width();
- float maxScaleY = smallestSize / (float) bounds.height();
+ float maxScaleX = smallestSize / bounds.width();
+ float maxScaleY = smallestSize / bounds.height();
float scale = Math.max(maxScaleX, maxScaleY);
float startScale = 1f;
if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index f300ef7..c84be4d 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -123,12 +123,12 @@
* @param ev MotionEvent in {@param eventSource}
*/
public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
- int[] point = new int[2];
- point[0] = (int) ev.getX();
- point[1] = (int) ev.getY();
+ float[] point = new float[2];
+ point[0] = ev.getX();
+ point[1] = ev.getY();
Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
// IF the MotionEvent is inside the thumb, container should not be pulled down.
- if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
+ if (mScrollbar.shouldBlockIntercept((int) point[0], (int) point[1])) {
return false;
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3611ad4..8291acc 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -142,7 +142,6 @@
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mActivity = ActivityContext.lookupContext(context);
- DeviceProfile grid = mActivity.getDeviceProfile();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
@@ -150,18 +149,24 @@
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
- int defaultIconSize = grid.iconSizePx;
+ final int defaultIconSize;
if (display == DISPLAY_WORKSPACE) {
+ DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+ defaultIconSize = grid.iconSizePx;
} else if (display == DISPLAY_ALL_APPS) {
+ DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
} else if (display == DISPLAY_FOLDER) {
+ DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
+ } else {
+ defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
}
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 9d9a639..fe6bbc0 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -57,12 +57,14 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.lang.annotation.Retention;
@@ -76,7 +78,7 @@
import androidx.annotation.IntDef;
import androidx.core.view.ViewCompat;
-public class CellLayout extends ViewGroup {
+public class CellLayout extends ViewGroup implements Transposable {
public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
@@ -182,6 +184,7 @@
// Related to accessible drag and drop
private DragAndDropAccessibilityDelegate mTouchHelper;
private boolean mUseTouchHelper = false;
+ private RotationMode mRotationMode = RotationMode.NORMAL;
public CellLayout(Context context) {
this(context, null);
@@ -203,7 +206,7 @@
setClipToPadding(false);
mActivity = ActivityContext.lookupContext(context);
- DeviceProfile grid = mActivity.getDeviceProfile();
+ DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
@@ -317,6 +320,24 @@
}
}
+ public void setRotationMode(RotationMode mode) {
+ if (mRotationMode != mode) {
+ mRotationMode = mode;
+ requestLayout();
+ }
+ }
+
+ @Override
+ public RotationMode getRotationMode() {
+ return mRotationMode;
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ mRotationMode.mapRect(left, top, right, bottom, mTempRect);
+ super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
+ }
+
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// Always attempt to dispatch hover events to accessibility first.
@@ -745,6 +766,14 @@
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
+
+ mShortcutsAndWidgets.setRotation(mRotationMode.surfaceRotation);
+ if (mRotationMode.isTransposed) {
+ int tmp = childWidthSize;
+ childWidthSize = childHeightSize;
+ childHeightSize = tmp;
+ }
+
if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
@@ -787,7 +816,6 @@
int top = getPaddingTop();
int bottom = b - t - getPaddingBottom();
- mShortcutsAndWidgets.layout(left, top, right, bottom);
// Expand the background drawing bounds by the padding baked into the background drawable
mBackground.getPadding(mTempRect);
mBackground.setBounds(
@@ -795,6 +823,16 @@
top - mTempRect.top - getPaddingTop(),
right + mTempRect.right + getPaddingRight(),
bottom + mTempRect.bottom + getPaddingBottom());
+
+ if (mRotationMode.isTransposed) {
+ int halfW = mShortcutsAndWidgets.getMeasuredWidth() / 2;
+ int halfH = mShortcutsAndWidgets.getMeasuredHeight() / 2;
+ int cX = (left + right) / 2;
+ int cY = (top + bottom) / 2;
+ mShortcutsAndWidgets.layout(cX - halfW, cY - halfH, cX + halfW, cY + halfH);
+ } else {
+ mShortcutsAndWidgets.layout(left, top, right, bottom);
+ }
}
/**
@@ -929,7 +967,7 @@
if (resize) {
cellToRect(cellX, cellY, spanX, spanY, r);
if (v instanceof LauncherAppWidgetHostView) {
- DeviceProfile profile = mActivity.getDeviceProfile();
+ DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
} else {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 6a3a26f..c0affb9 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -419,6 +419,10 @@
updateWorkspacePadding();
}
+ /**
+ * The current device insets. This is generally same as the insets being dispatched to
+ * {@link Insettable} elements, but can differ if the element is using a different profile.
+ */
public Rect getInsets() {
return mInsets;
}
@@ -582,45 +586,6 @@
}
}
- /**
- * Gets an item's location on the home screen. This is useful if the home screen
- * is animating, otherwise use {@link View#getLocationOnScreen(int[])}.
- * @param pageDiff The page difference relative to the current page.
- */
- public void getItemLocation(int cellX, int cellY, int spanX, int spanY, int container,
- int pageDiff, Rect outBounds) {
- outBounds.setEmpty();
- if (container == CONTAINER_HOTSEAT) {
- final int actualHotseatCellHeight;
- if (isVerticalBarLayout()) {
- actualHotseatCellHeight = availableHeightPx / inv.numRows;
- if (mIsSeascape) {
- outBounds.left = mHotseatPadding.left;
- } else {
- outBounds.left = availableWidthPx - hotseatBarSizePx + mHotseatPadding.left;
- }
- outBounds.right = outBounds.left + iconSizePx;
- outBounds.top = mHotseatPadding.top
- + actualHotseatCellHeight * (inv.numRows - cellX - 1);
- outBounds.bottom = outBounds.top + actualHotseatCellHeight;
- } else {
- actualHotseatCellHeight = hotseatBarSizePx - hotseatBarBottomPaddingPx
- - hotseatBarTopPaddingPx;
- outBounds.left = mInsets.left + workspacePadding.left + cellLayoutPaddingLeftRightPx
- + (cellX * getCellSize().x);
- outBounds.right = outBounds.left + getCellSize().x;
- outBounds.top = mInsets.top + availableHeightPx - hotseatBarSizePx;
- outBounds.bottom = outBounds.top + actualHotseatCellHeight;
- }
- } else {
- outBounds.left = mInsets.left + workspacePadding.left + cellLayoutPaddingLeftRightPx
- + (cellX * getCellSize().x) + (pageDiff * availableWidthPx);
- outBounds.right = outBounds.left + (getCellSize().x * spanX);
- outBounds.top = mInsets.top + workspacePadding.top + (cellY * getCellSize().y);
- outBounds.bottom = outBounds.top + (getCellSize().y * spanY);
- }
- }
-
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 4da7907..00acdcd 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -26,11 +26,13 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.views.Transposable;
-public class Hotseat extends CellLayout implements LogContainerProvider, Insettable {
+public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable {
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mHasVerticalHotseat;
@@ -77,7 +79,8 @@
@Override
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- DeviceProfile grid = mActivity.getDeviceProfile();
+ DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
+ insets = grid.getInsets();
if (grid.isVerticalBarLayout()) {
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -105,4 +108,9 @@
// Don't let if follow through to workspace
return true;
}
+
+ @Override
+ public RotationMode getRotationMode() {
+ return Launcher.getLauncher(getContext()).getRotationMode();
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ddaaa5d..417c5a2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -53,6 +53,7 @@
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -71,6 +72,7 @@
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
@@ -93,6 +95,7 @@
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -272,6 +275,9 @@
private float mCurrentAssistantVisibility = 0f;
+ private DeviceProfile mStableDeviceProfile;
+ private RotationMode mRotationMode = RotationMode.NORMAL;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
@@ -390,6 +396,12 @@
}
}
});
+
+ if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
+ WindowManager.LayoutParams lp = getWindow().getAttributes();
+ lp.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+ getWindow().setAttributes(lp);
+ }
}
@Override
@@ -418,6 +430,10 @@
@Override
protected void reapplyUi() {
+ if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
+ mRotationMode = mStableDeviceProfile == null ? RotationMode.NORMAL :
+ (mDeviceProfile.isSeascape() ? RotationMode.SEASCAPE : RotationMode.LANDSCAPE);
+ }
getRootView().dispatchInsets();
getStateManager().reapplyState(true /* cancelCurrentAnimation */);
}
@@ -469,8 +485,41 @@
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
+
+ if (FeatureFlags.FAKE_LANDSCAPE_UI.get() && mDeviceProfile.isVerticalBarLayout()
+ && !mDeviceProfile.isMultiWindowMode) {
+ mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
+ mRotationMode = mDeviceProfile.isSeascape()
+ ? RotationMode.SEASCAPE : RotationMode.LANDSCAPE;
+ } else {
+ mStableDeviceProfile = null;
+ mRotationMode = RotationMode.NORMAL;
+ }
+
onDeviceProfileInitiated();
- mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout(), true);
+ mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true);
+ }
+
+ public void updateInsets(Rect insets) {
+ mDeviceProfile.updateInsets(insets);
+ if (mStableDeviceProfile != null) {
+ mStableDeviceProfile.updateInsets(insets);
+ }
+ }
+
+ @Override
+ public RotationMode getRotationMode() {
+ return mRotationMode;
+ }
+
+ /**
+ * Device profile to be used by UI elements which are shown directly on top of the wallpaper
+ * and whose presentation is tied to the wallpaper (and physical device) and not the activity
+ * configuration.
+ */
+ @Override
+ public DeviceProfile getWallpaperDeviceProfile() {
+ return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile;
}
public RotationHelper getRotationHelper() {
@@ -1812,7 +1861,7 @@
mAppWidgetHost.clearViews();
if (mHotseat != null) {
- mHotseat.resetLayout(mDeviceProfile.isVerticalBarLayout());
+ mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
}
TraceHelper.endSection("startBinding");
}
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 8140026..20eec05 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -81,7 +81,7 @@
UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0);
// Update device profile before notifying th children.
- mLauncher.getDeviceProfile().updateInsets(insets);
+ mLauncher.updateInsets(insets);
boolean resetState = !insets.equals(mInsets);
setInsets(insets);
@@ -114,7 +114,7 @@
}
public void dispatchInsets() {
- mLauncher.getDeviceProfile().updateInsets(mInsets);
+ mLauncher.updateInsets(mInsets);
super.setInsets(mInsets);
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 30f418d..1bd8263 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -93,7 +93,7 @@
public void setupLp(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (child instanceof LauncherAppWidgetHostView) {
- DeviceProfile profile = mActivity.getDeviceProfile();
+ DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
profile.appWidgetScale.x, profile.appWidgetScale.y);
} else {
@@ -108,12 +108,12 @@
public int getCellContentHeight() {
return Math.min(getMeasuredHeight(),
- mActivity.getDeviceProfile().getCellHeight(mContainerType));
+ mActivity.getWallpaperDeviceProfile().getCellHeight(mContainerType));
}
public void measureChild(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- final DeviceProfile profile = mActivity.getDeviceProfile();
+ final DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
if (child instanceof LauncherAppWidgetHostView) {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 382eedd..26364be 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -16,9 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WallpaperManager;
@@ -63,13 +60,12 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.io.Closeable;
@@ -97,7 +93,6 @@
private static final int[] sLoc0 = new int[2];
private static final int[] sLoc1 = new int[2];
- private static final float[] sPoint = new float[2];
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
@@ -143,7 +138,7 @@
*/
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
- TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+ TimeUnit.SECONDS, new LinkedBlockingQueue<>());
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
@@ -170,37 +165,60 @@
* assumption fails, we will need to return a pair of scale factors.
*/
public static float getDescendantCoordRelativeToAncestor(
- View descendant, View ancestor, int[] coord, boolean includeRootScroll) {
- sPoint[0] = coord[0];
- sPoint[1] = coord[1];
+ View descendant, View ancestor, float[] coord, boolean includeRootScroll) {
+ return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll,
+ false);
+ }
+ /**
+ * Given a coordinate relative to the descendant, find the coordinate in a parent view's
+ * coordinates.
+ *
+ * @param descendant The descendant to which the passed coordinate is relative.
+ * @param ancestor The root view to make the coordinates relative to.
+ * @param coord The coordinate that we want mapped.
+ * @param includeRootScroll Whether or not to account for the scroll of the descendant:
+ * sometimes this is relevant as in a child's coordinates within the descendant.
+ * @param ignoreTransform If true, view transform is ignored
+ * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
+ * this scale factor is assumed to be equal in X and Y, and so if at any point this
+ * assumption fails, we will need to return a pair of scale factors.
+ */
+ public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor,
+ float[] coord, boolean includeRootScroll, boolean ignoreTransform) {
float scale = 1.0f;
View v = descendant;
while(v != ancestor && v != null) {
// For TextViews, scroll has a meaning which relates to the text position
// which is very strange... ignore the scroll.
if (v != descendant || includeRootScroll) {
- sPoint[0] -= v.getScrollX();
- sPoint[1] -= v.getScrollY();
+ offsetPoints(coord, -v.getScrollX(), -v.getScrollY());
}
- v.getMatrix().mapPoints(sPoint);
- sPoint[0] += v.getLeft();
- sPoint[1] += v.getTop();
+ if (ignoreTransform) {
+ if (v instanceof Transposable) {
+ RotationMode m = ((Transposable) v).getRotationMode();
+ if (m.isTransposed) {
+ sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY());
+ sMatrix.mapPoints(coord);
+ }
+ }
+ } else {
+ v.getMatrix().mapPoints(coord);
+ }
+ offsetPoints(coord, v.getLeft(), v.getTop());
scale *= v.getScaleX();
v = (View) v.getParent();
}
-
- coord[0] = Math.round(sPoint[0]);
- coord[1] = Math.round(sPoint[1]);
return scale;
}
+
/**
- * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, int[], boolean)}.
+ * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
*/
- public static void mapCoordInSelfToDescendant(View descendant, View root, int[] coord) {
+ public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
sMatrix.reset();
View v = descendant;
while(v != root) {
@@ -211,12 +229,23 @@
}
sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
sMatrix.invert(sInverseMatrix);
+ sInverseMatrix.mapPoints(coord);
+ }
- sPoint[0] = coord[0];
- sPoint[1] = coord[1];
- sInverseMatrix.mapPoints(sPoint);
- coord[0] = Math.round(sPoint[0]);
- coord[1] = Math.round(sPoint[1]);
+ /**
+ * Sets {@param out} to be same as {@param in} by rounding individual values
+ */
+ public static void roundArray(float[] in, int[] out) {
+ for (int i = 0; i < in.length; i++) {
+ out[i] = Math.round(in[i]);
+ }
+ }
+
+ public static void offsetPoints(float[] points, float offsetX, float offsetY) {
+ for (int i = 0; i < points.length; i += 2) {
+ points[i] += offsetX;
+ points[i + 1] += offsetY;
+ }
}
/**
@@ -569,53 +598,6 @@
return String.format(Locale.ENGLISH, "%d,%d", x, y);
}
- /**
- * Returns the location bounds of a view.
- * - For DeepShortcutView, we return the bounds of the icon view.
- * - For BubbleTextView, we return the icon bounds.
- */
- public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
- final DragLayer dragLayer = launcher.getDragLayer();
- final boolean isBubbleTextView = v instanceof BubbleTextView;
- final boolean isFolderIcon = v instanceof FolderIcon;
- final Rect rect = new Rect();
-
- // Deep shortcut views have their icon drawn in a separate view.
- final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
- if (v instanceof DeepShortcutView) {
- dragLayer.getDescendantRectRelativeToSelf(((DeepShortcutView) v).getIconView(), rect);
- } else if (fromDeepShortcutView) {
- DeepShortcutView view = (DeepShortcutView) v.getParent();
- dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
- } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
- && (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
- || ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
- CellLayout pageViewIsOn = ((CellLayout) v.getParent().getParent());
- int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);
-
- DeviceProfile dp = launcher.getDeviceProfile();
- ItemInfo info = ((ItemInfo) v.getTag());
- dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
- info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
- } else {
- dragLayer.getDescendantRectRelativeToSelf(v, rect);
- }
- int viewLocationLeft = rect.left;
- int viewLocationTop = rect.top;
-
- if (isBubbleTextView && !fromDeepShortcutView) {
- ((BubbleTextView) v).getIconBounds(rect);
- } else if (isFolderIcon) {
- ((FolderIcon) v).getPreviewBounds(rect);
- } else {
- rect.set(0, 0, rect.width(), rect.height());
- }
- viewLocationLeft += rect.left;
- viewLocationTop += rect.top;
- outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + rect.width(),
- viewLocationTop + rect.height());
- }
-
public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
try {
context.unregisterReceiver(receiver);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f1c183b..0f4c42d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -82,6 +82,7 @@
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -175,7 +176,9 @@
@Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
+ private final Rect mTempRect = new Rect();
private final int[] mTempXY = new int[2];
+ private final float[] mTempFXY = new float[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
private final float[] mTempTouchCoordinates = new float[2];
@@ -285,18 +288,23 @@
@Override
public void setInsets(Rect insets) {
- mInsets.set(insets);
-
DeviceProfile grid = mLauncher.getDeviceProfile();
- mMaxDistanceForFolderCreation = grid.isTablet
- ? 0.75f * grid.iconSizePx
- : 0.55f * grid.iconSizePx;
+ DeviceProfile stableGrid = mLauncher.getWallpaperDeviceProfile();
+
+ mMaxDistanceForFolderCreation = stableGrid.isTablet
+ ? 0.75f * stableGrid.iconSizePx
+ : 0.55f * stableGrid.iconSizePx;
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
- Rect padding = grid.workspacePadding;
- setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ Rect padding = stableGrid.workspacePadding;
- if (grid.shouldFadeAdjacentWorkspaceScreens()) {
+ RotationMode rotationMode = mLauncher.getRotationMode();
+
+ rotationMode.mapRect(padding, mTempRect);
+ setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
+ rotationMode.mapRect(insets, mInsets);
+
+ if (mWorkspaceFadeInAdjacentScreens) {
// In landscape mode the page spacing is set to the default.
setPageSpacing(grid.edgeMarginPx);
} else {
@@ -306,11 +314,13 @@
setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1));
}
- int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
- int paddingBottom = grid.cellLayoutBottomPaddingPx;
+
+ int paddingLeftRight = stableGrid.cellLayoutPaddingLeftRightPx;
+ int paddingBottom = stableGrid.cellLayoutBottomPaddingPx;
for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
- mWorkspaceScreens.valueAt(i)
- .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+ CellLayout page = mWorkspaceScreens.valueAt(i);
+ page.setRotationMode(rotationMode);
+ page.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
}
}
@@ -330,7 +340,7 @@
float scale = 1;
if (isWidget) {
- DeviceProfile profile = mLauncher.getDeviceProfile();
+ DeviceProfile profile = mLauncher.getWallpaperDeviceProfile();
scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
size[0] = r.width();
@@ -550,8 +560,10 @@
// created CellLayout.
CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
- int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
- int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
+ DeviceProfile grid = mLauncher.getWallpaperDeviceProfile();
+ int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
+ int paddingBottom = grid.cellLayoutBottomPaddingPx;
+ newScreen.setRotationMode(mLauncher.getRotationMode());
newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
mWorkspaceScreens.put(screenId, newScreen);
@@ -1930,18 +1942,9 @@
if (child == null) {
return;
}
+
ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
-
- // Use the absolute left instead of the child left, as we want the visible area
- // irrespective of the visible child. Since the view can only scroll horizontally, the
- // top position is not affected.
- mTempXY[0] = getPaddingLeft() + boundingLayout.getLeft();
- mTempXY[1] = child.getTop() + boundingLayout.getTop();
-
- float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
- outArea.set(mTempXY[0], mTempXY[1],
- (int) (mTempXY[0] + scale * boundingLayout.getMeasuredWidth()),
- (int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea);
}
@Override
@@ -2094,14 +2097,14 @@
}
boolean isPointInSelfOverHotseat(int x, int y) {
- mTempXY[0] = x;
- mTempXY[1] = y;
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
+ mTempFXY[0] = x;
+ mTempFXY[1] = y;
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
View hotseat = mLauncher.getHotseat();
- return mTempXY[0] >= hotseat.getLeft() &&
- mTempXY[0] <= hotseat.getRight() &&
- mTempXY[1] >= hotseat.getTop() &&
- mTempXY[1] <= hotseat.getBottom();
+ return mTempFXY[0] >= hotseat.getLeft() &&
+ mTempFXY[0] <= hotseat.getRight() &&
+ mTempFXY[1] >= hotseat.getTop() &&
+ mTempFXY[1] <= hotseat.getBottom();
}
/**
@@ -2111,13 +2114,8 @@
*/
private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
if (mLauncher.isHotseatLayout(layout)) {
- mTempXY[0] = (int) xy[0];
- mTempXY[1] = (int) xy[1];
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
- mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, mTempXY);
-
- xy[0] = mTempXY[0];
- xy[1] = mTempXY[1];
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
+ mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
} else {
mapPointFromSelfToChild(layout, xy);
}
@@ -2608,13 +2606,14 @@
DeviceProfile profile = mLauncher.getDeviceProfile();
Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
- loc[0] = r.left;
- loc[1] = r.top;
+ mTempFXY[0] = r.left;
+ mTempFXY[1] = r.top;
setFinalTransitionTransform();
float cellLayoutScale =
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, mTempFXY, true);
resetTransitionTransform();
+ Utilities.roundArray(mTempFXY, loc);
if (scale) {
float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 99a8801..8d0259d 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -93,14 +93,16 @@
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
- // Set the hotseat's pivot point to match the workspace's, so that it scales together.
- DragLayer dragLayer = mLauncher.getDragLayer();
- int[] workspacePivot = new int[]{(int) mWorkspace.getPivotX(),
- (int) mWorkspace.getPivotY()};
- dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
- dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
- hotseat.setPivotX(workspacePivot[0]);
- hotseat.setPivotY(workspacePivot[1]);
+ if (!hotseat.getRotationMode().isTransposed) {
+ // Set the hotseat's pivot point to match the workspace's, so that it scales together.
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ float[] workspacePivot =
+ new float[]{ mWorkspace.getPivotX(), mWorkspace.getPivotY() };
+ dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
+ dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
+ hotseat.setPivotX(workspacePivot[0]);
+ hotseat.setPivotY(workspacePivot[1]);
+ }
float hotseatScale = hotseatScaleAndTranslation.scale;
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator);
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index a55ea82..70df97a 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -110,6 +110,10 @@
"ENABLE_HINTS_IN_OVERVIEW", false,
"Show chip hints and gleams on the overview screen");
+ public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
+ "FAKE_LANDSCAPE_UI", false,
+ "Rotate launcher UI instead of using transposed layout");
+
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6950a1f..8de2f57 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -17,6 +17,10 @@
package com.android.launcher3.dragndrop;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.getMode;
+import static android.view.View.MeasureSpec.getSize;
+
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
@@ -29,12 +33,14 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
@@ -42,6 +48,7 @@
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
@@ -52,6 +59,7 @@
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.views.Transposable;
import java.util.ArrayList;
@@ -260,10 +268,10 @@
Rect r = new Rect();
getViewRectRelativeToSelf(dragView, r);
- int coord[] = new int[2];
+ float coord[] = new float[2];
float childScale = child.getScaleX();
- coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
- coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
+ coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2);
+ coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2);
// Since the child hasn't necessarily been laid out, we force the lp to be updated with
// the correct coordinates (above) and use these to determine the final location
@@ -271,8 +279,8 @@
// We need to account for the scale of the child itself, as the above only accounts for
// for the scale in parents.
scale *= childScale;
- int toX = coord[0];
- int toY = coord[1];
+ int toX = Math.round(coord[0]);
+ int toY = Math.round(coord[1]);
float toScale = scale;
if (child instanceof TextView) {
TextView tv = (TextView) child;
@@ -551,4 +559,159 @@
public WorkspaceAndHotseatScrim getScrim() {
return mScrim;
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ RotationMode rotation = mActivity.getRotationMode();
+ int count = getChildCount();
+
+ if (!rotation.isTransposed
+ || getMode(widthMeasureSpec) != EXACTLY
+ || getMode(heightMeasureSpec) != EXACTLY) {
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ child.setRotation(rotation.surfaceRotation);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ } else {
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ if (!(child instanceof Transposable)) {
+ measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+ } else {
+ measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0);
+
+ child.setPivotX(child.getMeasuredWidth() / 2);
+ child.setPivotY(child.getMeasuredHeight() / 2);
+ child.setRotation(rotation.surfaceRotation);
+ }
+ }
+ setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec));
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ RotationMode rotation = mActivity.getRotationMode();
+ if (!rotation.isTransposed) {
+ super.onLayout(changed, left, top, right, bottom);
+ return;
+ }
+
+ final int count = getChildCount();
+
+ final int parentWidth = right - left;
+ final int parentHeight = bottom - top;
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
+
+ if (lp instanceof LayoutParams) {
+ final LayoutParams dlp = (LayoutParams) lp;
+ if (dlp.customPosition) {
+ child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height);
+ continue;
+ }
+ }
+
+ final int width = child.getMeasuredWidth();
+ final int height = child.getMeasuredHeight();
+
+ int childLeft;
+ int childTop;
+
+ int gravity = lp.gravity;
+ if (gravity == -1) {
+ gravity = Gravity.TOP | Gravity.START;
+ }
+
+ final int layoutDirection = getLayoutDirection();
+
+ int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
+ int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+ int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ if (child instanceof Transposable) {
+ if (rotation == RotationMode.SEASCAPE) {
+ if (horizontalGravity == Gravity.RIGHT) {
+ horizontalGravity = Gravity.LEFT;
+ } else if (horizontalGravity == Gravity.LEFT) {
+ horizontalGravity = Gravity.RIGHT;
+ }
+
+ if (verticalGravity == Gravity.TOP) {
+ verticalGravity = Gravity.BOTTOM;
+ } else if (verticalGravity == Gravity.BOTTOM) {
+ verticalGravity = Gravity.TOP;
+ }
+ }
+
+ switch (horizontalGravity) {
+ case Gravity.CENTER_HORIZONTAL:
+ childTop = (parentHeight - height) / 2 +
+ lp.topMargin - lp.bottomMargin;
+ break;
+ case Gravity.RIGHT:
+ childTop = width / 2 + lp.rightMargin - height / 2;
+ break;
+ case Gravity.LEFT:
+ default:
+ childTop = parentHeight - lp.leftMargin - width / 2 - height / 2;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.CENTER_VERTICAL:
+ childLeft = (parentWidth - width) / 2 +
+ lp.leftMargin - lp.rightMargin;
+ break;
+ case Gravity.BOTTOM:
+ childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin;
+ break;
+ case Gravity.TOP:
+ default:
+ childLeft = height / 2 - width / 2 + lp.topMargin;
+ }
+ } else {
+ switch (horizontalGravity) {
+ case Gravity.CENTER_HORIZONTAL:
+ childLeft = (parentWidth - width) / 2 +
+ lp.leftMargin - lp.rightMargin;
+ break;
+ case Gravity.RIGHT:
+ childLeft = parentWidth - width - lp.rightMargin;
+ break;
+ case Gravity.LEFT:
+ default:
+ childLeft = lp.leftMargin;
+ }
+
+ switch (verticalGravity) {
+ case Gravity.TOP:
+ childTop = lp.topMargin;
+ break;
+ case Gravity.CENTER_VERTICAL:
+ childTop = (parentHeight - height) / 2 +
+ lp.topMargin - lp.bottomMargin;
+ break;
+ case Gravity.BOTTOM:
+ childTop = parentHeight - height - lp.bottomMargin;
+ break;
+ default:
+ childTop = lp.topMargin;
+ }
+ }
+
+ child.layout(childLeft, childTop, childLeft + width, childTop + height);
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 4dbff1c..f2eae17 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -869,7 +869,7 @@
DeviceProfile grid = mLauncher.getDeviceProfile();
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
+ DragLayer parent = mLauncher.getDragLayer();
int width = getFolderWidth();
int height = getFolderHeight();
@@ -881,8 +881,7 @@
// We need to bound the folder to the currently visible workspace area
if (mLauncher.getStateManager().getState().overviewUi) {
- mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
- sTempRect);
+ parent.getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(), sTempRect);
} else {
mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 02242a3..6fa9ba9 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -159,7 +159,7 @@
"is dependent on this");
}
- DeviceProfile grid = launcher.getDeviceProfile();
+ DeviceProfile grid = launcher.getWallpaperDeviceProfile();
FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
.inflate(resId, group, false);
@@ -174,7 +174,7 @@
icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
- icon.mDotRenderer = launcher.getDeviceProfile().mDotRenderer;
+ icon.mDotRenderer = grid.mDotRenderer;
icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
Folder folder = Folder.fromXml(launcher);
folder.setDragController(launcher.getDragController());
@@ -508,7 +508,8 @@
public void drawDot(Canvas canvas) {
if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
Rect iconBounds = mDotParams.iconBounds;
- BubbleTextView.getIconBounds(this, iconBounds, mLauncher.getDeviceProfile().iconSizePx);
+ BubbleTextView.getIconBounds(this, iconBounds,
+ mLauncher.getWallpaperDeviceProfile().iconSizePx);
// If we are animating to the accepting state, animate the dot out.
mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index ac908f4..46df77a 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -42,7 +42,6 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
/**
@@ -135,7 +134,7 @@
mBgColor = ta.getColor(R.styleable.FolderIconPreview_android_colorPrimary, 0);
ta.recycle();
- DeviceProfile grid = activity.getDeviceProfile();
+ DeviceProfile grid = activity.getWallpaperDeviceProfile();
previewSize = grid.folderIconSizePx;
basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
diff --git a/src/com/android/launcher3/graphics/RotationMode.java b/src/com/android/launcher3/graphics/RotationMode.java
new file mode 100644
index 0000000..1b2cbdb
--- /dev/null
+++ b/src/com/android/launcher3/graphics/RotationMode.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2019 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.graphics;
+
+import android.graphics.Rect;
+
+public abstract class RotationMode {
+
+ public final float surfaceRotation;
+ public final boolean isTransposed;
+
+ private RotationMode(float surfaceRotation) {
+ this.surfaceRotation = surfaceRotation;
+ isTransposed = surfaceRotation != 0;
+ }
+
+ public final void mapRect(Rect rect, Rect out) {
+ mapRect(rect.left, rect.top, rect.right, rect.bottom, out);
+ }
+
+ public void mapRect(int left, int top, int right, int bottom, Rect out) {
+ out.set(left, top, right, bottom);
+ }
+
+ public static RotationMode NORMAL = new RotationMode(0) { };
+
+ public static RotationMode LANDSCAPE = new RotationMode(-90) {
+ @Override
+ public void mapRect(int left, int top, int right, int bottom, Rect out) {
+ out.left = top;
+ out.top = right;
+ out.right = bottom;
+ out.bottom = left;
+ }
+ };
+
+ public static RotationMode SEASCAPE = new RotationMode(90) {
+ @Override
+ public void mapRect(int left, int top, int right, int bottom, Rect out) {
+ out.left = bottom;
+ out.top = left;
+ out.right = top;
+ out.bottom = right;
+ }
+ };
+}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index c9cdeff..0331a86 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -22,6 +22,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.dot.DotInfo;
/**
@@ -56,6 +57,19 @@
DeviceProfile getDeviceProfile();
+ /**
+ * Device profile to be used by UI elements which are shown directly on top of the wallpaper
+ * and whose presentation is tied to the wallpaper (and physical device) and not the activity
+ * configuration.
+ */
+ default DeviceProfile getWallpaperDeviceProfile() {
+ return getDeviceProfile();
+ }
+
+ default RotationMode getRotationMode() {
+ return RotationMode.NORMAL;
+ }
+
static ActivityContext lookupContext(Context context) {
if (context instanceof ActivityContext) {
return (ActivityContext) context;
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 86e1e32..3c81bcf 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -88,7 +88,8 @@
// Touch is being dispatched through a proxy from InputMonitor
private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
- protected final int[] mTmpXY = new int[2];
+ protected final float[] mTmpXY = new float[2];
+ protected final float[] mTmpRectPoints = new float[4];
protected final Rect mHitRect = new Rect();
@ViewDebug.ExportedProperty(category = "launcher")
@@ -306,14 +307,16 @@
* @return The factor by which this descendant is scaled relative to this DragLayer.
*/
public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
- mTmpXY[0] = 0;
- mTmpXY[1] = 0;
- float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
-
- r.set(mTmpXY[0], mTmpXY[1],
- (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
- (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
- return scale;
+ mTmpRectPoints[0] = 0;
+ mTmpRectPoints[1] = 0;
+ mTmpRectPoints[2] = descendant.getWidth();
+ mTmpRectPoints[3] = descendant.getHeight();
+ float s = getDescendantCoordRelativeToSelf(descendant, mTmpRectPoints);
+ r.left = Math.round(Math.min(mTmpRectPoints[0], mTmpRectPoints[2]));
+ r.top = Math.round(Math.min(mTmpRectPoints[1], mTmpRectPoints[3]));
+ r.right = Math.round(Math.max(mTmpRectPoints[0], mTmpRectPoints[2]));
+ r.bottom = Math.round(Math.max(mTmpRectPoints[1], mTmpRectPoints[3]));
+ return s;
}
public float getLocationInDragLayer(View child, int[] loc) {
@@ -323,6 +326,14 @@
}
public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+ mTmpXY[0] = coord[0];
+ mTmpXY[1] = coord[1];
+ float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
+ Utilities.roundArray(mTmpXY, coord);
+ return scale;
+ }
+
+ public float getDescendantCoordRelativeToSelf(View descendant, float[] coord) {
return getDescendantCoordRelativeToSelf(descendant, coord, false);
}
@@ -338,17 +349,27 @@
* this scale factor is assumed to be equal in X and Y, and so if at any point this
* assumption fails, we will need to return a pair of scale factors.
*/
- public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
+ public float getDescendantCoordRelativeToSelf(View descendant, float[] coord,
boolean includeRootScroll) {
return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
coord, includeRootScroll);
}
/**
+ * Inverse of {@link #getDescendantCoordRelativeToSelf(View, float[])}.
+ */
+ public void mapCoordInSelfToDescendant(View descendant, float[] coord) {
+ Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
+ }
+
+ /**
* Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
*/
public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
- Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
+ mTmpXY[0] = coord[0];
+ mTmpXY[1] = coord[1];
+ Utilities.mapCoordInSelfToDescendant(descendant, this, mTmpXY);
+ Utilities.roundArray(mTmpXY, coord);
}
public void getViewRectRelativeToSelf(View v, Rect r) {
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 77f278a..2574665 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -23,6 +26,7 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -61,12 +65,10 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
-import static com.android.launcher3.Utilities.mapToRange;
-
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
-
+@TargetApi(Build.VERSION_CODES.Q)
public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
public static final float SHAPE_PROGRESS_DURATION = 0.15f;
@@ -186,14 +188,16 @@
* @param v The view to copy
* @param positionOut Rect that will hold the size and position of v.
*/
- private void matchPositionOf(Launcher launcher, View v, Rect positionOut) {
- Utilities.getLocationBoundsForView(launcher, v, positionOut);
- final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
+ private void matchPositionOf(Launcher launcher, View v, RectF positionOut) {
+ getLocationBoundsForView(launcher, v, positionOut);
+ final LayoutParams lp = new LayoutParams(
+ Math.round(positionOut.width()),
+ Math.round(positionOut.height()));
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
- lp.leftMargin = positionOut.left;
- lp.topMargin = positionOut.top;
+ lp.leftMargin = Math.round(positionOut.left);
+ lp.topMargin = Math.round(positionOut.top);
setLayoutParams(lp);
// Set the properties here already to make sure they are available when running the first
// animation frame.
@@ -201,6 +205,57 @@
+ lp.height);
}
+ /**
+ * Returns the location bounds of a view.
+ * - For DeepShortcutView, we return the bounds of the icon view.
+ * - For BubbleTextView, we return the icon bounds.
+ */
+ private void getLocationBoundsForView(Launcher launcher, View v, RectF outRect) {
+ final boolean isBubbleTextView = v instanceof BubbleTextView;
+ final boolean isFolderIcon = v instanceof FolderIcon;
+
+ // Deep shortcut views have their icon drawn in a separate view.
+ final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
+
+ final View targetView;
+ boolean ignoreTransform = false;
+
+ if (v instanceof DeepShortcutView) {
+ targetView = ((DeepShortcutView) v).getIconView();
+ } else if (fromDeepShortcutView) {
+ DeepShortcutView view = (DeepShortcutView) v.getParent();
+ targetView = view.getIconView();
+ } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
+ && (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
+ || ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
+ targetView = v;
+ ignoreTransform = true;
+ } else {
+ targetView = v;
+ }
+
+ float[] points = new float[] {0, 0, targetView.getWidth(), targetView.getHeight()};
+ Utilities.getDescendantCoordRelativeToAncestor(targetView, launcher.getDragLayer(), points,
+ false, ignoreTransform);
+
+ float viewLocationLeft = Math.min(points[0], points[2]);
+ float viewLocationTop = Math.min(points[1], points[3]);
+
+ final Rect iconRect = new Rect();
+ if (isBubbleTextView && !fromDeepShortcutView) {
+ ((BubbleTextView) v).getIconBounds(iconRect);
+ } else if (isFolderIcon) {
+ ((FolderIcon) v).getPreviewBounds(iconRect);
+ } else {
+ iconRect.set(0, 0, Math.abs(Math.round(points[2] - points[0])),
+ Math.abs(Math.round(points[3] - points[1])));
+ }
+ viewLocationLeft += iconRect.left;
+ viewLocationTop += iconRect.top;
+ outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + iconRect.width(),
+ viewLocationTop + iconRect.height());
+ }
+
@WorkerThread
private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening,
Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) {
@@ -411,7 +466,7 @@
* @param isOpening True if this view replaces the icon for app open animation.
*/
public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
- boolean hideOriginal, Rect positionOut, boolean isOpening, FloatingIconView recycle) {
+ boolean hideOriginal, RectF positionOut, boolean isOpening, FloatingIconView recycle) {
if (recycle != null) {
recycle.recycle();
}
diff --git a/src/com/android/launcher3/views/Transposable.java b/src/com/android/launcher3/views/Transposable.java
new file mode 100644
index 0000000..929c1aa
--- /dev/null
+++ b/src/com/android/launcher3/views/Transposable.java
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2019 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.views;
+
+import com.android.launcher3.graphics.RotationMode;
+
+/**
+ * Indicates that a view can be transposed.
+ */
+public interface Transposable {
+
+ RotationMode getRotationMode();
+}