Loading res/values/dimens.xml +5 −2 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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> Loading Loading @@ -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> Loading src/com/android/launcher3/ButtonDropTarget.java +45 −39 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -306,11 +310,47 @@ public abstract class ButtonDropTarget extends TextView if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) { mTextVisible = isVisible; setText(newText); 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(mDrawable, null, null, null); int drawablePadding = mTextVisible ? mDrawablePadding : 0; setCompoundDrawablePadding(drawablePadding); } setCompoundDrawablesRelative(mIconVisible ? mDrawable : null, null, null, null); setCompoundDrawablePadding(mIconVisible && mTextVisible ? mDrawablePadding : 0); } @Override Loading @@ -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()); Loading src/com/android/launcher3/DeviceProfile.java +9 −10 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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. */ Loading @@ -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; } Loading Loading @@ -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())); } Loading src/com/android/launcher3/DropTargetBar.java +97 −104 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading Loading @@ -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); } } Loading @@ -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); } } } Loading @@ -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]; ButtonDropTarget leftButton = mTempTargets[0]; leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0, barCenter - (buttonGap / 2), leftButton.getMeasuredHeight()); ButtonDropTarget rightButton = visibleButtons[1]; ButtonDropTarget rightButton = mTempTargets[1]; rightButton.layout(barCenter + (buttonGap / 2), 0, barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2), barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(), 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()); } } } 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++; } } Loading Loading
res/values/dimens.xml +5 −2 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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> Loading Loading @@ -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> Loading
src/com/android/launcher3/ButtonDropTarget.java +45 −39 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -306,11 +310,47 @@ public abstract class ButtonDropTarget extends TextView if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) { mTextVisible = isVisible; setText(newText); 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(mDrawable, null, null, null); int drawablePadding = mTextVisible ? mDrawablePadding : 0; setCompoundDrawablePadding(drawablePadding); } setCompoundDrawablesRelative(mIconVisible ? mDrawable : null, null, null, null); setCompoundDrawablePadding(mIconVisible && mTextVisible ? mDrawablePadding : 0); } @Override Loading @@ -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()); Loading
src/com/android/launcher3/DeviceProfile.java +9 −10 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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; Loading Loading @@ -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. */ Loading @@ -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; } Loading Loading @@ -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())); } Loading
src/com/android/launcher3/DropTargetBar.java +97 −104 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading Loading @@ -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); } } Loading @@ -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); } } } Loading @@ -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]; ButtonDropTarget leftButton = mTempTargets[0]; leftButton.layout(barCenter - leftButton.getMeasuredWidth() - (buttonGap / 2), 0, barCenter - (buttonGap / 2), leftButton.getMeasuredHeight()); ButtonDropTarget rightButton = visibleButtons[1]; ButtonDropTarget rightButton = mTempTargets[1]; rightButton.layout(barCenter + (buttonGap / 2), 0, barCenter + rightButton.getMeasuredWidth() + (buttonGap / 2), barCenter + (buttonGap / 2) + rightButton.getMeasuredWidth(), 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()); } } } 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++; } } Loading