Merge "Update drop target button alignment across devices" into tm-dev am: 618047355d

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/18557384

Change-Id: I55c0d50a9930643ed84faaf60b382ebe37a2b437
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Alex Chau
2022-05-23 18:53:45 +00:00
committed by Automerger Merge Worker
4 changed files with 157 additions and 156 deletions

View File

@@ -24,6 +24,8 @@
<dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
<!-- Minimum space between workspace and hotseat in spring loaded mode -->
<dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
<!-- Minimum amount of next page visible in spring loaded mode -->
<dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen>
<dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
<dimen name="cell_layout_padding">10.77dp</dimen>
@@ -58,9 +60,10 @@
<!-- Drop target bar -->
<dimen name="dynamic_grid_drop_target_size">56dp</dimen>
<dimen name="drop_target_vertical_gap">20dp</dimen>
<dimen name="drop_target_top_margin">36dp</dimen>
<dimen name="drop_target_top_margin">32dp</dimen>
<dimen name="drop_target_bottom_margin">16dp</dimen>
<!-- App Widget resize frame -->
<!-- Button drop target bar -->
<dimen name="button_drop_target_min_text_size">10sp</dimen>
<dimen name="button_drop_target_resize_text_increment">1sp</dimen>
@@ -237,7 +240,7 @@
<dimen name="drop_target_button_drawable_padding">8dp</dimen>
<dimen name="drop_target_button_drawable_horizontal_padding">16dp</dimen>
<dimen name="drop_target_button_drawable_vertical_padding">8dp</dimen>
<dimen name="drop_target_button_gap">22dp</dimen>
<dimen name="drop_target_button_gap">28dp</dimen>
<!-- the distance an icon must be dragged before button drop targets accept it -->
<dimen name="drag_distanceThreshold">30dp</dimen>

View File

@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -49,6 +50,8 @@ public abstract class ButtonDropTarget extends TextView
private static final int[] sTempCords = new int[2];
private static final int DRAG_VIEW_DROP_DURATION = 285;
private static final float DRAG_VIEW_HOVER_OVER_OPACITY = 0.65f;
private static final int MAX_LINES_TEXT_MULTI_LINE = 2;
private static final int MAX_LINES_TEXT_SINGLE_LINE = 1;
public static final int TOOLTIP_DEFAULT = 0;
public static final int TOOLTIP_LEFT = 1;
@@ -72,6 +75,8 @@ public abstract class ButtonDropTarget extends TextView
protected CharSequence mText;
protected Drawable mDrawable;
private boolean mTextVisible = true;
private boolean mIconVisible = true;
private boolean mTextMultiLine = true;
private PopupWindow mToolTip;
private int mToolTipLocation;
@@ -109,8 +114,7 @@ public abstract class ButtonDropTarget extends TextView
// drawableLeft and drawableStart.
mDrawable = getContext().getDrawable(resId).mutate();
mDrawable.setTintList(getTextColors());
centerIcon();
setCompoundDrawablesRelative(mDrawable, null, null, null);
updateIconVisibility();
}
public void setDropTargetBar(DropTargetBar dropTargetBar) {
@@ -306,13 +310,49 @@ public abstract class ButtonDropTarget extends TextView
if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
mTextVisible = isVisible;
setText(newText);
centerIcon();
setCompoundDrawablesRelative(mDrawable, null, null, null);
int drawablePadding = mTextVisible ? mDrawablePadding : 0;
setCompoundDrawablePadding(drawablePadding);
updateIconVisibility();
}
}
/**
* Display button text over multiple lines when isMultiLine is true, single line otherwise.
*/
public void setTextMultiLine(boolean isMultiLine) {
if (mTextMultiLine != isMultiLine) {
mTextMultiLine = isMultiLine;
setSingleLine(!isMultiLine);
setMaxLines(isMultiLine ? MAX_LINES_TEXT_MULTI_LINE : MAX_LINES_TEXT_SINGLE_LINE);
int inputType = InputType.TYPE_CLASS_TEXT;
if (isMultiLine) {
inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
}
setInputType(inputType);
}
}
protected boolean isTextMultiLine() {
return mTextMultiLine;
}
/**
* Sets the button icon visible when isVisible is true, hides it otherwise.
*/
public void setIconVisible(boolean isVisible) {
if (mIconVisible != isVisible) {
mIconVisible = isVisible;
updateIconVisibility();
}
}
private void updateIconVisibility() {
if (mIconVisible) {
centerIcon();
}
setCompoundDrawablesRelative(mIconVisible ? mDrawable : null, null, null, null);
setCompoundDrawablePadding(mIconVisible && mTextVisible ? mDrawablePadding : 0);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -324,40 +364,6 @@ public abstract class ButtonDropTarget extends TextView
hideTooltip();
}
/**
* Reduce the size of the text until it fits or reaches a minimum.
*
* The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and
* it diminishes by intervals defined by
* {@code R.dimen.button_drop_target_resize_text_increment}
* This functionality is very similar to the option
* {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because
* the layout width is {@code WRAP_CONTENT}.
*
* @param availableWidth Available width in the button to fit the text, used in
* {@code ButtonDropTarget#isTextTruncated(int)}
* @return The biggest text size in SP that makes the text fit or if the text can't fit returns
* the min available value
*/
public float resizeTextToFit(int availableWidth) {
float minSize = Utilities.pxToSp(getResources()
.getDimensionPixelSize(R.dimen.button_drop_target_min_text_size));
float step = Utilities.pxToSp(getResources()
.getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment));
float textSize = Utilities.pxToSp(getTextSize());
while (textSize > minSize) {
if (isTextTruncated(availableWidth)) {
textSize -= step;
setTextSize(textSize);
} else {
return textSize;
}
}
return minSize;
}
public boolean isTextTruncated(int availableWidth) {
availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
+ getCompoundDrawablePadding());

View File

@@ -108,6 +108,7 @@ public class DeviceProfile {
public float workspaceSpringLoadShrunkTop;
public float workspaceSpringLoadShrunkBottom;
public final int workspaceSpringLoadedBottomSpace;
public final int workspaceSpringLoadedMinNextPageVisiblePx;
private final int extraSpace;
public int workspaceTopPadding;
@@ -344,6 +345,8 @@ public class DeviceProfile {
workspaceSpringLoadedBottomSpace =
res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize(
R.dimen.dynamic_grid_spring_loaded_min_next_space_visible);
workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
@@ -499,7 +502,7 @@ public class DeviceProfile {
*/
private int calculateQsbWidth() {
if (isQsbInline) {
int columns = isTwoPanels ? inv.numColumns * 2 : inv.numColumns;
int columns = getPanelCount() * inv.numColumns;
return getIconToIconWidthForColumns(columns)
- iconSizePx * numShownHotseatIcons
- hotseatBorderSpace * numShownHotseatIcons;
@@ -951,13 +954,6 @@ public class DeviceProfile {
return workspaceSpringLoadShrunkBottom;
}
/**
* Gets the minimum visible amount of the next workspace page when in the spring-loaded state.
*/
private float getWorkspaceSpringLoadedMinimumNextPageVisible() {
return getCellSize().x / 2f;
}
/**
* Gets the scale of the workspace for the spring-loaded edit state.
*/
@@ -969,8 +965,7 @@ public class DeviceProfile {
// Reduce scale if next pages would not be visible after scaling the workspace
int workspaceWidth = availableWidthPx;
float scaledWorkspaceWidth = workspaceWidth * scale;
float maxAvailableWidth =
workspaceWidth - (2 * getWorkspaceSpringLoadedMinimumNextPageVisible());
float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx);
if (scaledWorkspaceWidth > maxAvailableWidth) {
scale *= maxAvailableWidth / scaledWorkspaceWidth;
}
@@ -1414,6 +1409,10 @@ public class DeviceProfile {
prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
workspaceSpringLoadShrunkBottom));
writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace",
workspaceSpringLoadedBottomSpace));
writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx",
workspaceSpringLoadedMinNextPageVisiblePx));
writer.println(
prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
}

View File

@@ -39,8 +39,6 @@ import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.testing.TestProtocol;
import java.util.Arrays;
/*
* The top bar containing various drop targets: Delete/App Info/Uninstall.
*/
@@ -53,6 +51,8 @@ public class DropTargetBar extends FrameLayout
private final Runnable mFadeAnimationEndRunnable =
() -> updateVisibility(DropTargetBar.this);
private final Launcher mLauncher;
@ViewDebug.ExportedProperty(category = "launcher")
protected boolean mDeferOnDragEnd;
@@ -60,16 +60,19 @@ public class DropTargetBar extends FrameLayout
protected boolean mVisible = false;
private ButtonDropTarget[] mDropTargets;
private ButtonDropTarget[] mTempTargets;
private ViewPropertyAnimator mCurrentAnimation;
private boolean mIsVertical = true;
public DropTargetBar(Context context, AttributeSet attrs) {
super(context, attrs);
mLauncher = Launcher.getLauncher(context);
}
public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(context);
}
@Override
@@ -80,12 +83,13 @@ public class DropTargetBar extends FrameLayout
mDropTargets[i] = (ButtonDropTarget) getChildAt(i);
mDropTargets[i].setDropTargetBar(this);
}
mTempTargets = new ButtonDropTarget[getChildCount()];
}
@Override
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
DeviceProfile grid = mLauncher.getDeviceProfile();
mIsVertical = grid.isVerticalBarLayout();
lp.leftMargin = insets.left;
@@ -116,10 +120,15 @@ public class DropTargetBar extends FrameLayout
lp.height = grid.dropTargetBarSizePx;
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
DeviceProfile dp = mLauncher.getDeviceProfile();
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
int verticalPadding = dp.dropTargetVerticalPaddingPx;
setLayoutParams(lp);
for (ButtonDropTarget button : mDropTargets) {
button.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.dropTargetTextSizePx);
button.setToolTipLocation(tooltipLocation);
button.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
verticalPadding);
}
}
@@ -135,36 +144,69 @@ public class DropTargetBar extends FrameLayout
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
int visibleCount = getVisibleButtonsCount();
if (visibleCount > 0) {
int availableWidth = width / visibleCount;
boolean textVisible = true;
boolean textResized = false;
float textSize = mDropTargets[0].getTextSize();
for (ButtonDropTarget button : mDropTargets) {
if (button.getVisibility() == GONE) {
continue;
}
if (button.isTextTruncated(availableWidth)) {
textSize = Math.min(textSize, button.resizeTextToFit(availableWidth));
textResized = true;
}
textVisible = textVisible && !button.isTextTruncated(availableWidth);
}
int visibleCount = getVisibleButtons(mTempTargets);
if (visibleCount == 1) {
int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
if (textResized) {
for (ButtonDropTarget button : mDropTargets) {
button.setTextSize(textSize);
}
ButtonDropTarget firstButton = mTempTargets[0];
firstButton.setTextVisible(true);
firstButton.setIconVisible(true);
firstButton.measure(widthSpec, heightSpec);
} else if (visibleCount == 2) {
DeviceProfile dp = mLauncher.getDeviceProfile();
int verticalPadding = dp.dropTargetVerticalPaddingPx;
int horizontalPadding = dp.dropTargetHorizontalPaddingPx;
ButtonDropTarget firstButton = mTempTargets[0];
firstButton.setTextVisible(true);
firstButton.setIconVisible(true);
firstButton.setTextMultiLine(false);
// Reset second button padding in case it was previously changed to multi-line text.
firstButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
verticalPadding);
ButtonDropTarget secondButton = mTempTargets[1];
secondButton.setTextVisible(true);
secondButton.setIconVisible(true);
secondButton.setTextMultiLine(false);
// Reset second button padding in case it was previously changed to multi-line text.
secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding,
verticalPadding);
float scale = dp.getWorkspaceSpringLoadScale();
int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale);
int availableWidth;
if (dp.isTwoPanels) {
// Both buttons for two panel fit to the width of one Cell Layout (less
// half of the center gap between the buttons).
int halfButtonGap = dp.dropTargetGapPx / 2;
availableWidth = scaledPanelWidth - halfButtonGap / 2;
} else {
// Both buttons plus the button gap do not display past the edge of the scaled
// workspace.
availableWidth = (scaledPanelWidth - dp.dropTargetGapPx) / 2;
}
int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
for (ButtonDropTarget button : mDropTargets) {
if (button.getVisibility() != GONE) {
button.setTextVisible(textVisible);
button.measure(widthSpec, heightSpec);
firstButton.measure(widthSpec, heightSpec);
secondButton.measure(widthSpec, heightSpec);
if (!mIsVertical) {
// Remove icons and put the button's text on two lines if text is truncated.
if (firstButton.isTextTruncated(availableWidth)) {
firstButton.setIconVisible(false);
firstButton.setTextMultiLine(true);
firstButton.setPadding(horizontalPadding, verticalPadding / 2,
horizontalPadding, verticalPadding / 2);
}
if (secondButton.isTextTruncated(availableWidth)) {
secondButton.setIconVisible(false);
secondButton.setTextMultiLine(true);
secondButton.setPadding(horizontalPadding, verticalPadding / 2,
horizontalPadding, verticalPadding / 2);
}
}
}
@@ -173,98 +215,49 @@ public class DropTargetBar extends FrameLayout
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int visibleCount = getVisibleButtonsCount();
int visibleCount = getVisibleButtons(mTempTargets);
if (visibleCount == 0) {
return;
}
Launcher launcher = Launcher.getLauncher(getContext());
Workspace<?> workspace = launcher.getWorkspace();
DeviceProfile dp = launcher.getDeviceProfile();
int buttonHorizontalPadding = dp.dropTargetHorizontalPaddingPx;
int buttonVerticalPadding = dp.dropTargetVerticalPaddingPx;
int barCenter = (right - left) / 2;
ButtonDropTarget[] visibleButtons = Arrays.stream(mDropTargets)
.filter(b -> b.getVisibility() != GONE)
.toArray(ButtonDropTarget[]::new);
Arrays.stream(visibleButtons).forEach(
b -> b.setPadding(buttonHorizontalPadding, buttonVerticalPadding,
buttonHorizontalPadding, buttonVerticalPadding));
DeviceProfile dp = mLauncher.getDeviceProfile();
// Center vertical bar over scaled workspace, accounting for hotseat offset.
float scale = dp.getWorkspaceSpringLoadScale();
Workspace<?> ws = mLauncher.getWorkspace();
int barCenter;
if (dp.isTwoPanels) {
barCenter = (right - left) / 2;
} else {
int workspaceCenter = (ws.getLeft() + ws.getRight()) / 2;
int cellLayoutCenter = ((dp.getInsets().left + dp.workspacePadding.left) + (dp.widthPx
- dp.getInsets().right - dp.workspacePadding.right)) / 2;
int cellLayoutCenterOffset = (int) ((cellLayoutCenter - workspaceCenter) * scale);
barCenter = workspaceCenter + cellLayoutCenterOffset - left;
}
if (visibleCount == 1) {
ButtonDropTarget button = visibleButtons[0];
ButtonDropTarget button = mTempTargets[0];
button.layout(barCenter - (button.getMeasuredWidth() / 2), 0,
barCenter + (button.getMeasuredWidth() / 2), button.getMeasuredHeight());
} else if (visibleCount == 2) {
int buttonGap = dp.dropTargetGapPx;
if (dp.isTwoPanels) {
ButtonDropTarget leftButton = visibleButtons[0];
leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
ButtonDropTarget leftButton = mTempTargets[0];
leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0,
barCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
ButtonDropTarget rightButton = visibleButtons[1];
rightButton.layout(barCenter + (buttonGap / 2), 0,
barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
rightButton.getMeasuredHeight());
} else if (dp.isTablet) {
int numberOfMargins = visibleCount - 1;
int buttonWidths = Arrays.stream(mDropTargets)
.filter(b -> b.getVisibility() != GONE)
.mapToInt(ButtonDropTarget::getMeasuredWidth)
.sum();
int totalWidth = buttonWidths + (numberOfMargins * buttonGap);
int buttonsStartMargin = barCenter - (totalWidth / 2);
int start = buttonsStartMargin;
for (ButtonDropTarget button : visibleButtons) {
int margin = (start != buttonsStartMargin) ? buttonGap : 0;
button.layout(start + margin, 0, start + margin + button.getMeasuredWidth(),
button.getMeasuredHeight());
start += button.getMeasuredWidth() + margin;
}
} else if (mIsVertical) {
// Center buttons over workspace, not screen.
int verticalCenter = (workspace.getRight() - workspace.getLeft()) / 2;
ButtonDropTarget leftButton = visibleButtons[0];
leftButton.layout(verticalCenter - leftButton.getMeasuredWidth() - (buttonGap / 2),
0, verticalCenter - (buttonGap / 2), leftButton.getMeasuredHeight());
ButtonDropTarget rightButton = visibleButtons[1];
rightButton.layout(verticalCenter + (buttonGap / 2), 0,
verticalCenter + rightButton.getMeasuredWidth() + (buttonGap / 2),
rightButton.getMeasuredHeight());
} else if (dp.isPhone) {
// Buttons aligned to outer edges of scaled workspace.
float scale = dp.getWorkspaceSpringLoadScale();
int workspaceWidth = (int) (launcher.getWorkspace().getNormalChildWidth() * scale);
int start = barCenter - (workspaceWidth / 2);
int end = barCenter + (workspaceWidth / 2);
ButtonDropTarget leftButton = visibleButtons[0];
ButtonDropTarget rightButton = visibleButtons[1];
// If the text within the buttons is too long, the buttons can overlap
int overlap = start + leftButton.getMeasuredWidth() + rightButton.getMeasuredWidth()
- end;
if (overlap > 0) {
end += overlap;
}
leftButton.layout(start, 0, start + leftButton.getMeasuredWidth(),
leftButton.getMeasuredHeight());
rightButton.layout(end - rightButton.getMeasuredWidth(), 0, end,
rightButton.getMeasuredHeight());
}
ButtonDropTarget rightButton = mTempTargets[1];
rightButton.layout(barCenter + (buttonGap / 2), 0,
barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(),
rightButton.getMeasuredHeight());
}
}
private int getVisibleButtonsCount() {
private int getVisibleButtons(ButtonDropTarget[] outVisibleButtons) {
int visibleCount = 0;
for (ButtonDropTarget buttons : mDropTargets) {
if (buttons.getVisibility() != GONE) {
for (ButtonDropTarget button : mDropTargets) {
if (button.getVisibility() != GONE) {
outVisibleButtons[visibleCount] = button;
visibleCount++;
}
}