Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15ca7e75 authored by Alex Chau's avatar Alex Chau Committed by Automerger Merge Worker
Browse files

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

parents 84f3b75c 61804735
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -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>
+45 −39
Original line number Diff line number Diff line
@@ -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,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
@@ -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());
+9 −10
Original line number Diff line number Diff line
@@ -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()));
    }
+97 −104
Original line number Diff line number Diff line
@@ -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];
            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++;
            }
        }