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

Commit 25f8e406 authored by Sebastian Franco's avatar Sebastian Franco
Browse files

Adding ReorderParameters to ReorderAlgorithm

No logic change made.

Flag: NA
Bug: 229292911
Test: ReorderAlgorithmUnitTest
Change-Id: Iababd2fba688a482cffe6d2243c987b0c022c6a5
parent b70b7fde
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ import com.android.launcher3.celllayout.CellPosMapper.CellPos;
import com.android.launcher3.celllayout.DelegatedCellDrawing;
import com.android.launcher3.celllayout.ItemConfiguration;
import com.android.launcher3.celllayout.ReorderAlgorithm;
import com.android.launcher3.celllayout.ReorderParameters;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
@@ -1748,8 +1749,11 @@ public class CellLayout extends ViewGroup {
    protected ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
            int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
            ItemConfiguration solution) {
        return createReorderAlgorithm().findReorderSolution(pixelX, pixelY, minSpanX, minSpanY,
                spanX, spanY, direction, dragView, decX, solution);
        ItemConfiguration configuration = new ItemConfiguration();
        copyCurrentStateToSolution(configuration);
        ReorderParameters parameters = new ReorderParameters(pixelX, pixelY, spanX, spanY, minSpanX,
                minSpanY, dragView, configuration);
        return createReorderAlgorithm().findReorderSolution(parameters, decX);
    }

    public void copyCurrentStateToSolution(ItemConfiguration solution) {
@@ -1779,8 +1783,12 @@ public class CellLayout extends ViewGroup {
     */
    public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX, int minSpanY,
            int spanX, int spanY, View dragView) {
        return createReorderAlgorithm().calculateReorder(pixelX, pixelY, minSpanX, minSpanY,
                spanX, spanY, dragView);
        ItemConfiguration configuration = new ItemConfiguration();
        copyCurrentStateToSolution(configuration);
        return createReorderAlgorithm().calculateReorder(
                new ReorderParameters(pixelX, pixelY, spanX, spanY,  minSpanX, minSpanY, dragView,
                        configuration)
        );
    }

    int[] performReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY,
+10 −14
Original line number Diff line number Diff line
@@ -48,28 +48,24 @@ public class MulticellReorderAlgorithm extends ReorderAlgorithm {
    }

    @Override
    public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX,
            int minSpanY, int spanX, int spanY) {
    public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
        return removeSeamFromSolution(simulateSeam(
                () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX,
                        spanY)));
                () -> super.closestEmptySpaceReorder(reorderParameters))
        );
    }

    @Override
    public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
            int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
            ItemConfiguration solution) {
    public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
            boolean decX) {
        return removeSeamFromSolution(simulateSeam(
                () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
                        direction, dragView, decX, solution)));
                () -> super.findReorderSolution(reorderParameters, decX)));
    }

    @Override
    public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
            int spanY,
            View dragView) {
        return removeSeamFromSolution(simulateSeam(
                () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView)));
    public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
        return removeSeamFromSolution(
                simulateSeam(() -> super.dropInPlaceSolution(reorderParameters))
        );
    }

    void addSeam() {
+90 −107
Original line number Diff line number Diff line
@@ -45,36 +45,28 @@ public class ReorderAlgorithm {
     * This method differs from closestEmptySpaceReorder and dropInPlaceSolution because this method
     * will move items around and will change the shape of the item if possible to try to find a
     * solution.
     *
     * <p>
     * When changing the size of the widget this method will try first subtracting -1 in the x
     * dimension and then subtracting -1 in the y dimension until finding a possible solution or
     * until it no longer can reduce the span.
     *
     * @param pixelX    X coordinate in pixels in the screen
     * @param pixelY    Y coordinate in pixels in the screen
     * @param minSpanX  minimum possible horizontal span it will try to find a solution for.
     * @param minSpanY  minimum possible vertical span it will try to find a solution for.
     * @param spanX     horizontal cell span
     * @param spanY     vertical cell span
     * @param direction direction in which it will try to push the items intersecting the desired
     *                  view
     * @param dragView  view being dragged in reorder
     * @param decX     whether it will decrease the horizontal or vertical span if it can't find a
     *                 solution for the current span.
     * @param solution  variable to store the solution
     * @return the same solution variable
     */
    public ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX,
            int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
            ItemConfiguration solution) {
        return findReorderSolutionRecursive(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY,
                direction, dragView, decX, solution);
    public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
            boolean decX) {
        return findReorderSolutionRecursive(reorderParameters.getPixelX(),
                reorderParameters.getPixelY(), reorderParameters.getMinSpanX(),
                reorderParameters.getMinSpanY(), reorderParameters.getSpanX(),
                reorderParameters.getSpanY(), mCellLayout.mDirectionVector,
                reorderParameters.getDragView(), decX, reorderParameters.getSolution());
    }


    private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY,
            int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView,
            boolean decX, ItemConfiguration solution) {
    private ItemConfiguration findReorderSolutionRecursive(int pixelX, int pixelY, int minSpanX,
            int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX,
            ItemConfiguration solution) {
        // Copy the current state into the solution. This solution will be manipulated as necessary.
        mCellLayout.copyCurrentStateToSolution(solution);
        // Copy the current occupied array into the temporary occupied array. This array will be
@@ -89,8 +81,8 @@ public class ReorderAlgorithm {
        boolean success;
        // First we try the exact nearest position of the item being dragged,
        // we will then want to try to move this around to other neighbouring positions
        success = rearrangementExists(result[0], result[1], spanX, spanY, direction,
                dragView, solution);
        success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
                solution);

        if (!success) {
            // We try shrinking the widget down to size in an alternating pattern, shrink 1 in
@@ -135,10 +127,11 @@ public class ReorderAlgorithm {
        // and not by the views hash which is "random".
        // The views are sorted twice, once for the X position and a second time for the Y position
        // to ensure same order everytime.
        Comparator comparator = Comparator.comparing(view ->
                        ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX())
                .thenComparing(view ->
                        ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY());
        Comparator comparator = Comparator.comparing(
                view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellX()
        ).thenComparing(
                view -> ((CellLayoutLayoutParams) ((View) view).getLayoutParams()).getCellY()
        );
        List<View> views = solution.map.keySet().stream().sorted(comparator).toList();
        for (View child : views) {
            if (child == ignoreView) continue;
@@ -158,15 +151,13 @@ public class ReorderAlgorithm {
        // First we try to find a solution which respects the push mechanic. That is,
        // we try to find a solution such that no displaced item travels through another item
        // without also displacing that item.
        if (attemptPushInDirection(intersectingViews, occupiedRect, direction,
                ignoreView,
        if (attemptPushInDirection(intersectingViews, occupiedRect, direction, ignoreView,
                solution)) {
            return true;
        }

        // Next we try moving the views as a block, but without requiring the push mechanic.
        if (addViewsToTempLocation(intersectingViews, occupiedRect, direction,
                ignoreView,
        if (addViewsToTempLocation(intersectingViews, occupiedRect, direction, ignoreView,
                solution)) {
            return true;
        }
@@ -180,8 +171,8 @@ public class ReorderAlgorithm {
        return true;
    }

    private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
            int[] direction, ItemConfiguration currentState) {
    private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop, int[] direction,
            ItemConfiguration currentState) {
        CellAndSpan c = currentState.map.get(v);
        boolean success = false;
        mCellLayout.mTmpOccupied.markCells(c, false);
@@ -305,16 +296,16 @@ public class ReorderAlgorithm {
            int temp = direction[1];
            direction[1] = 0;

            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }
            direction[1] = temp;
            temp = direction[0];
            direction[0] = 0;

            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }
            // Revert the direction
@@ -325,16 +316,16 @@ public class ReorderAlgorithm {
            direction[1] *= -1;
            temp = direction[1];
            direction[1] = 0;
            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }

            direction[1] = temp;
            temp = direction[0];
            direction[0] = 0;
            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }
            // revert the direction
@@ -345,15 +336,15 @@ public class ReorderAlgorithm {
        } else {
            // If the direction vector has a single non-zero component, we push first in the
            // direction of the vector
            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }
            // Then we try the opposite direction
            direction[0] *= -1;
            direction[1] *= -1;
            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }
            // Switch the direction back
@@ -367,16 +358,16 @@ public class ReorderAlgorithm {
            int temp = direction[1];
            direction[1] = direction[0];
            direction[0] = temp;
            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }

            // Then we try the opposite direction
            direction[0] *= -1;
            direction[1] *= -1;
            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                    ignoreView, solution)) {
            if (pushViewsToTempLocation(intersectingViews, occupied, direction, ignoreView,
                    solution)) {
                return true;
            }
            // Switch the direction back
@@ -446,63 +437,59 @@ public class ReorderAlgorithm {
    /**
     * Returns a "reorder" if there is empty space without rearranging anything.
     *
     * @param pixelX   X coordinate in pixels in the screen
     * @param pixelY   Y coordinate in pixels in the screen
     * @param spanX    horizontal cell span
     * @param spanY    vertical cell span
     * @param dragView view being dragged in reorder
     * @return the configuration that represents the found reorder
     */
    public ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX,
            int spanY, View dragView) {
        int[] result = mCellLayout.findNearestAreaIgnoreOccupied(pixelX, pixelY, spanX, spanY,
                new int[2]);
    public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
        int[] result = mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(),
                reorderParameters.getPixelY(), reorderParameters.getSpanX(),
                reorderParameters.getSpanY(), new int[2]);
        ItemConfiguration solution = new ItemConfiguration();
        mCellLayout.copyCurrentStateToSolution(solution);

        solution.isSolution = !isConfigurationRegionOccupied(
                new Rect(result[0], result[1], result[0] + spanX, result[1] + spanY),
                solution,
                dragView
        );
                new Rect(result[0], result[1], result[0] + reorderParameters.getSpanX(),
                        result[1] + reorderParameters.getSpanY()), solution,
                reorderParameters.getDragView());
        if (!solution.isSolution) {
            return solution;
        }
        solution.cellX = result[0];
        solution.cellY = result[1];
        solution.spanX = spanX;
        solution.spanY = spanY;
        solution.spanX = reorderParameters.getSpanX();
        solution.spanY = reorderParameters.getSpanY();
        return solution;
    }

    private boolean isConfigurationRegionOccupied(Rect region,
            ItemConfiguration configuration, View ignoreView) {
        return configuration.map.entrySet()
    private boolean isConfigurationRegionOccupied(Rect region, ItemConfiguration configuration,
            View ignoreView) {
        return configuration.map
                .entrySet()
                .stream()
                .filter(entry -> entry.getKey() != ignoreView)
                .map(Entry::getValue)
                .anyMatch(cellAndSpan -> region.intersect(cellAndSpan.cellX, cellAndSpan.cellY,
                .anyMatch(cellAndSpan -> region.intersect(
                        cellAndSpan.cellX,
                        cellAndSpan.cellY,
                        cellAndSpan.cellX + cellAndSpan.spanX,
                        cellAndSpan.cellY + cellAndSpan.spanY));
                        cellAndSpan.cellY + cellAndSpan.spanY
                        )
                );
    }

    /**
     * Returns a "reorder" where we simply drop the item in the closest empty space, without moving
     * any other item in the way.
     *
     * @param pixelX X coordinate in pixels in the screen
     * @param pixelY Y coordinate in pixels in the screen
     * @param spanX  horizontal cell span
     * @param spanY  vertical cell span
     * @return the configuration that represents the found reorder
     */
    public ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY,
            int minSpanX, int minSpanY, int spanX, int spanY) {
    public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
        ItemConfiguration solution = new ItemConfiguration();
        int[] result = new int[2];
        int[] resultSpan = new int[2];
        mCellLayout.findNearestVacantArea(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, result,
                resultSpan);
        mCellLayout.findNearestVacantArea(reorderParameters.getPixelX(),
                reorderParameters.getPixelY(), reorderParameters.getMinSpanX(),
                reorderParameters.getMinSpanY(), reorderParameters.getSpanX(),
                reorderParameters.getSpanY(), result, resultSpan);
        if (result[0] >= 0 && result[1] >= 0) {
            mCellLayout.copyCurrentStateToSolution(solution);
            solution.cellX = result[0];
@@ -521,32 +508,19 @@ public class ReorderAlgorithm {
     * the workspace to make space for the new item, this function return a solution for that
     * reorder.
     *
     * @param pixelX   X coordinate in the screen of the dragView in pixels
     * @param pixelY   Y coordinate in the screen of the dragView in pixels
     * @param minSpanX minimum horizontal span the item can be shrunk to
     * @param minSpanY minimum vertical span the item can be shrunk to
     * @param spanX    occupied horizontal span
     * @param spanY    occupied vertical span
     * @param dragView the view of the item being draged
     * @return returns a solution for the given parameters, the solution contains all the icons and
     * the locations they should be in the given solution.
     */
    public ItemConfiguration calculateReorder(int pixelX, int pixelY, int minSpanX,
            int minSpanY, int spanX, int spanY, View dragView) {
        getDirectionVectorForDrop(pixelX, pixelY, spanX, spanY, dragView,
                mCellLayout.mDirectionVector);
    public ItemConfiguration calculateReorder(ReorderParameters reorderParameters) {
        getDirectionVectorForDrop(reorderParameters, mCellLayout.mDirectionVector);

        ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(pixelX, pixelY, spanX, spanY,
                dragView);
        ItemConfiguration dropInPlaceSolution = dropInPlaceSolution(reorderParameters);

        // Find a solution involving pushing / displacing any items in the way
        ItemConfiguration swapSolution = findReorderSolution(pixelX, pixelY, minSpanX,
                minSpanY, spanX, spanY, mCellLayout.mDirectionVector, dragView, true,
                new ItemConfiguration());
        ItemConfiguration swapSolution = findReorderSolution(reorderParameters, true);

        // We attempt the approach which doesn't shuffle views at all
        ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(pixelX, pixelY, minSpanX,
                minSpanY, spanX, spanY);
        ItemConfiguration closestSpaceSolution = closestEmptySpaceReorder(reorderParameters);

        // If the reorder solution requires resizing (shrinking) the item being dropped, we instead
        // favor a solution in which the item is not resized, but
@@ -586,21 +560,26 @@ public class ReorderAlgorithm {
     * those cells. Instead we use some heuristics to often lock the vector to up, down, left
     * or right, which helps make pushing feel right.
     */
    private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
            int spanY, View dragView, int[] resultDirection) {
    public void getDirectionVectorForDrop(ReorderParameters reorderParameters,
            int[] resultDirection) {

        //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
        int[] targetDestination = new int[2];

        mCellLayout.findNearestAreaIgnoreOccupied(dragViewCenterX, dragViewCenterY, spanX, spanY,
                targetDestination);
        mCellLayout.findNearestAreaIgnoreOccupied(reorderParameters.getPixelX(),
                reorderParameters.getPixelY(), reorderParameters.getSpanX(),
                reorderParameters.getSpanY(), targetDestination);
        Rect dragRect = new Rect();
        mCellLayout.cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
        dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
        mCellLayout.cellToRect(targetDestination[0], targetDestination[1],
                reorderParameters.getSpanX(), reorderParameters.getSpanY(), dragRect);
        dragRect.offset(reorderParameters.getPixelX() - dragRect.centerX(),
                reorderParameters.getPixelY() - dragRect.centerY());

        Rect region = new Rect(targetDestination[0], targetDestination[1],
                targetDestination[0] + spanX, targetDestination[1] + spanY);
        Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region, dragView);
                targetDestination[0] + reorderParameters.getSpanX(),
                targetDestination[1] + reorderParameters.getSpanY());
        Rect dropRegionRect = mCellLayout.getIntersectingRectanglesInRegion(region,
                reorderParameters.getDragView());
        if (dropRegionRect == null) dropRegionRect = new Rect(region);

        int dropRegionSpanX = dropRegionRect.width();
@@ -609,13 +588,17 @@ public class ReorderAlgorithm {
        mCellLayout.cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
                dropRegionRect.height(), dropRegionRect);

        int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
        int deltaY = (dropRegionRect.centerY() - dragViewCenterY) / spanY;
        int deltaX = (dropRegionRect.centerX() - reorderParameters.getPixelX())
                / reorderParameters.getSpanX();
        int deltaY = (dropRegionRect.centerY() - reorderParameters.getPixelY())
                / reorderParameters.getSpanY();

        if (dropRegionSpanX == mCellLayout.getCountX() || spanX == mCellLayout.getCountX()) {
        if (dropRegionSpanX == mCellLayout.getCountX()
                || reorderParameters.getSpanX() == mCellLayout.getCountX()) {
            deltaX = 0;
        }
        if (dropRegionSpanY == mCellLayout.getCountY() || spanY == mCellLayout.getCountY()) {
        if (dropRegionSpanY == mCellLayout.getCountY()
                || reorderParameters.getSpanY() == mCellLayout.getCountY()) {
            deltaY = 0;
        }

+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.celllayout

import android.view.View

class ReorderParameters(
    val pixelX: Int,
    val pixelY: Int,
    val spanX: Int,
    val spanY: Int,
    val minSpanX: Int,
    val minSpanY: Int,
    val dragView: View?,
    val solution: ItemConfiguration
) {}
+15 −3
Original line number Diff line number Diff line
@@ -191,9 +191,21 @@ public class ReorderAlgorithmUnitTest {

        int[] testCaseXYinPixels = new int[2];
        cl.regionToCenterPoint(x, y, spanX, spanY, testCaseXYinPixels);
        ItemConfiguration solution = cl.createReorderAlgorithm().calculateReorder(
                testCaseXYinPixels[0], testCaseXYinPixels[1], minSpanX, minSpanY, spanX, spanY,
                null);
        ItemConfiguration configuration = new ItemConfiguration();
        cl.copyCurrentStateToSolution(configuration);
        ItemConfiguration solution = cl.createReorderAlgorithm()
                .calculateReorder(
                        new ReorderParameters(
                                testCaseXYinPixels[0],
                                testCaseXYinPixels[1],
                                spanX,
                                spanY,
                                minSpanX,
                                minSpanY,
                                null,
                                configuration
                        )
                );
        if (solution == null) {
            solution = new ItemConfiguration();
            solution.isSolution = false;