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.util.GridOccupancy; 23 24 import java.util.function.Supplier; 25 26 /** 27 * Variant of ReorderAlgorithm which simulates a foldable screen and adds a seam in the middle 28 * to prevent items to be placed in the middle. 29 */ 30 public class MulticellReorderAlgorithm extends ReorderAlgorithm { 31 32 private final View mSeam; 33 MulticellReorderAlgorithm(CellLayout cellLayout)34 public MulticellReorderAlgorithm(CellLayout cellLayout) { 35 super(cellLayout); 36 mSeam = new View(cellLayout.getContext()); 37 } 38 removeSeamFromSolution( CellLayout.ItemConfiguration solution)39 private CellLayout.ItemConfiguration removeSeamFromSolution( 40 CellLayout.ItemConfiguration solution) { 41 solution.map.forEach((view, cell) -> cell.cellX = 42 cell.cellX > mCellLayout.getCountX() / 2 ? cell.cellX - 1 : cell.cellX); 43 solution.cellX = 44 solution.cellX > mCellLayout.getCountX() / 2 ? solution.cellX - 1 : solution.cellX; 45 return solution; 46 } 47 48 @Override closestEmptySpaceReorder(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY)49 public CellLayout.ItemConfiguration closestEmptySpaceReorder(int pixelX, int pixelY, 50 int minSpanX, int minSpanY, 51 int spanX, int spanY) { 52 return removeSeamFromSolution(simulateSeam( 53 () -> super.closestEmptySpaceReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, 54 spanY))); 55 } 56 57 @Override findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, CellLayout.ItemConfiguration solution)58 public CellLayout.ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, 59 int minSpanY, int spanX, int spanY, int[] direction, View dragView, boolean decX, 60 CellLayout.ItemConfiguration solution) { 61 return removeSeamFromSolution(simulateSeam( 62 () -> super.findReorderSolution(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, 63 direction, dragView, decX, solution))); 64 } 65 66 @Override dropInPlaceSolution(int pixelX, int pixelY, int spanX, int spanY, View dragView)67 public CellLayout.ItemConfiguration dropInPlaceSolution(int pixelX, int pixelY, int spanX, 68 int spanY, 69 View dragView) { 70 return removeSeamFromSolution(simulateSeam( 71 () -> super.dropInPlaceSolution(pixelX, pixelY, spanX, spanY, dragView))); 72 } 73 addSeam()74 void addSeam() { 75 MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout; 76 mcl.setSeamWasAdded(true); 77 CellLayoutLayoutParams lp = new CellLayoutLayoutParams(mcl.getCountX() / 2, 0, 1, 78 mcl.getCountY()); 79 lp.canReorder = false; 80 mcl.setCountX(mcl.getCountX() + 1); 81 mcl.getShortcutsAndWidgets().addViewInLayout(mSeam, lp); 82 mcl.setOccupied(createGridOccupancyWithSeam(mcl.getOccupied())); 83 mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY()); 84 } 85 removeSeam()86 void removeSeam() { 87 MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout; 88 mcl.setCountX(mcl.getCountX() - 1); 89 mcl.getShortcutsAndWidgets().removeViewInLayout(mSeam); 90 mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY()); 91 mcl.setSeamWasAdded(false); 92 } 93 94 /** 95 * The function supplied here will execute while the CellLayout has a simulated seam added. 96 * @param f function to run under simulation 97 * @param <T> return value of the supplied function 98 * @return Value of supplied function 99 */ simulateSeam(Supplier<T> f)100 public <T> T simulateSeam(Supplier<T> f) { 101 MultipageCellLayout mcl = (MultipageCellLayout) mCellLayout; 102 if (mcl.isSeamWasAdded()) { 103 return f.get(); 104 } 105 GridOccupancy auxGrid = mcl.getOccupied(); 106 addSeam(); 107 T res = f.get(); 108 removeSeam(); 109 mcl.setOccupied(auxGrid); 110 return res; 111 } 112 createGridOccupancyWithSeam(GridOccupancy gridOccupancy)113 GridOccupancy createGridOccupancyWithSeam(GridOccupancy gridOccupancy) { 114 GridOccupancy grid = new GridOccupancy(mCellLayout.getCountX(), mCellLayout.getCountY()); 115 for (int x = 0; x < mCellLayout.getCountX(); x++) { 116 for (int y = 0; y < mCellLayout.getCountY(); y++) { 117 int offset = x >= mCellLayout.getCountX() / 2 ? 1 : 0; 118 if (x == mCellLayout.getCountX() / 2) { 119 grid.cells[x][y] = true; 120 } else { 121 grid.cells[x][y] = gridOccupancy.cells[x - offset][y]; 122 } 123 } 124 } 125 return grid; 126 } 127 } 128