1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.launcher3.celllayout;
17 
18 import android.view.View;
19 
20 import com.android.launcher3.CellLayout;
21 import com.android.launcher3.MultipageCellLayout;
22 import com.android.launcher3.ShortcutAndWidgetContainer;
23 import com.android.launcher3.util.GridOccupancy;
24 
25 import java.util.Arrays;
26 import java.util.function.Supplier;
27 
28 /**
29  * Variant of ReorderAlgorithm which simulates a foldable screen and adds a seam in the middle
30  * to prevent items to be placed in the middle.
31  */
32 public class MulticellReorderAlgorithm extends ReorderAlgorithm {
33 
34     private final View mSeam;
35 
MulticellReorderAlgorithm(CellLayout cellLayout)36     public MulticellReorderAlgorithm(CellLayout cellLayout) {
37         super(cellLayout);
38         mSeam = new View(cellLayout.getContext());
39     }
40 
removeSeamFromSolution(ItemConfiguration solution)41     public ItemConfiguration removeSeamFromSolution(ItemConfiguration solution) {
42         solution.map.remove(mSeam);
43         solution.map.forEach((view, cell) -> cell.cellX =
44                 cell.cellX > mCellLayout.getCountX() / 2 ? cell.cellX - 1 : cell.cellX);
45         solution.cellX =
46                 solution.cellX > mCellLayout.getCountX() / 2 ? solution.cellX - 1 : solution.cellX;
47         return solution;
48     }
49 
50     @Override
closestEmptySpaceReorder(ReorderParameters reorderParameters)51     public ItemConfiguration closestEmptySpaceReorder(ReorderParameters reorderParameters) {
52         return removeSeamFromSolution(simulateSeam(
53                 () -> super.closestEmptySpaceReorder(reorderParameters))
54         );
55     }
56 
57     @Override
findReorderSolution(ReorderParameters reorderParameters, boolean decX)58     public ItemConfiguration findReorderSolution(ReorderParameters reorderParameters,
59             boolean decX) {
60         return removeSeamFromSolution(simulateSeam(
61                 () -> super.findReorderSolution(reorderParameters, decX)));
62     }
63 
64     @Override
dropInPlaceSolution(ReorderParameters reorderParameters)65     public ItemConfiguration dropInPlaceSolution(ReorderParameters reorderParameters) {
66         return removeSeamFromSolution(
67                 simulateSeam(() -> super.dropInPlaceSolution(reorderParameters))
68         );
69     }
70 
addSeam()71     void addSeam() {
72         MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout;
73         mcl.setSeamWasAdded(true);
74         CellLayoutLayoutParams lp = new CellLayoutLayoutParams(mcl.getCountX() / 2, 0, 1,
75                 mcl.getCountY());
76         lp.canReorder = false;
77         mcl.setCountX(mcl.getCountX() + 1);
78         mcl.getShortcutsAndWidgets().addViewInLayout(mSeam, lp);
79         mcl.setOccupied(createGridOccupancyWithSeam());
80         mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY());
81     }
82 
removeSeam()83     void removeSeam() {
84         MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout;
85         mcl.setCountX(mcl.getCountX() - 1);
86         mcl.getShortcutsAndWidgets().removeViewInLayout(mSeam);
87         mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY());
88         mcl.setSeamWasAdded(false);
89     }
90 
91     /**
92      * The function supplied here will execute while the CellLayout has a simulated seam added.
93      *
94      * @param f   function to run under simulation
95      * @param <T> return value of the supplied function
96      * @return Value of supplied function
97      */
simulateSeam(Supplier<T> f)98     public <T> T simulateSeam(Supplier<T> f) {
99         MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout;
100         if (mcl.isSeamWasAdded()) {
101             return f.get();
102         }
103         GridOccupancy auxGrid = mcl.getOccupied();
104         addSeam();
105         T res = f.get();
106         removeSeam();
107         mcl.setOccupied(auxGrid);
108         return res;
109     }
110 
createGridOccupancyWithSeam()111     GridOccupancy createGridOccupancyWithSeam() {
112         ShortcutAndWidgetContainer shortcutAndWidgets = mCellLayout.getShortcutsAndWidgets();
113         GridOccupancy grid = new GridOccupancy(mCellLayout.getCountX(), mCellLayout.getCountY());
114         for (int i = 0; i < shortcutAndWidgets.getChildCount(); i++) {
115             View view = shortcutAndWidgets.getChildAt(i);
116             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
117             int seamOffset = lp.getCellX() >= mCellLayout.getCountX() / 2 && lp.canReorder ? 1 : 0;
118             grid.markCells(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan, lp.cellVSpan,
119                     true);
120         }
121         Arrays.fill(grid.cells[mCellLayout.getCountX() / 2], true);
122         return grid;
123     }
124 }
125